Use m.accepted_terms account data
To remember what policies the user has agreed to
This commit is contained in:
parent
90a0f93215
commit
f77e7fc3e8
2 changed files with 62 additions and 8 deletions
58
src/Terms.js
58
src/Terms.js
|
@ -53,8 +53,10 @@ export function presentTermsForServices(services) {
|
||||||
/**
|
/**
|
||||||
* Start a flow where the user is presented with terms & conditions for some services
|
* Start a flow where the user is presented with terms & conditions for some services
|
||||||
*
|
*
|
||||||
* @param {function} interactionCallback Function called with an array of:
|
* @param {Service[]} services Object with keys 'serviceType', 'baseUrl', 'accessToken'
|
||||||
* { service: {Service}, terms: {terms response from API} }
|
* @param {function} interactionCallback Function called with:
|
||||||
|
* * an array of { service: {Service}, terms: {terms response from API} }
|
||||||
|
* * an array of URLs the user has already agreed to
|
||||||
* Must return a Promise which resolves with a list of URLs of documents agreed to
|
* Must return a Promise which resolves with a list of URLs of documents agreed to
|
||||||
* @returns {Promise} resolves when the user agreed to all necessary terms or rejects
|
* @returns {Promise} resolves when the user agreed to all necessary terms or rejects
|
||||||
* if they cancel.
|
* if they cancel.
|
||||||
|
@ -86,14 +88,57 @@ export async function startTermsFlow(services, interactionCallback) {
|
||||||
const terms = await Promise.all(termsPromises);
|
const terms = await Promise.all(termsPromises);
|
||||||
const policiesAndServicePairs = terms.map((t, i) => { return { 'service': services[i], 'policies': t.policies }; });
|
const policiesAndServicePairs = terms.map((t, i) => { return { 'service': services[i], 'policies': t.policies }; });
|
||||||
|
|
||||||
const agreedUrls = await interactionCallback(policiesAndServicePairs);
|
// fetch the set of agreed policy URLs from account data
|
||||||
console.log("User has agreed to URLs", agreedUrls);
|
const currentAcceptedTerms = await MatrixClientPeg.get().getAccountData('m.accepted_terms');
|
||||||
|
let agreedUrlSet;
|
||||||
|
if (!currentAcceptedTerms || !currentAcceptedTerms.getContent() || !currentAcceptedTerms.getContent().accepted) {
|
||||||
|
agreedUrlSet = new Set();
|
||||||
|
} else {
|
||||||
|
agreedUrlSet = new Set(currentAcceptedTerms.getContent().accepted);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove any policies the user has already agreed to and any services where
|
||||||
|
// they've already agreed to all the policies
|
||||||
|
// NB. it could be nicer to show the user stuff they've already agreed to,
|
||||||
|
// but then they'd assume they can un-check the boxes to un-agree to a policy,
|
||||||
|
// but that is not a thing the API supports, so probably best to just show
|
||||||
|
// things they've not agreed to yet.
|
||||||
|
const unagreedPoliciesAndServicePairs = [];
|
||||||
|
for (const {service, policies} of policiesAndServicePairs) {
|
||||||
|
const unagreedPolicies = {};
|
||||||
|
for (const [policyName, policy] of Object.entries(policies)) {
|
||||||
|
let policyAgreed = false;
|
||||||
|
for (const lang of Object.keys(policy)) {
|
||||||
|
if (lang === 'version') continue;
|
||||||
|
if (agreedUrlSet.has(policy[lang].url)) {
|
||||||
|
policyAgreed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!policyAgreed) unagreedPolicies[policyName] = policy;
|
||||||
|
}
|
||||||
|
if (Object.keys(unagreedPolicies).length > 0) {
|
||||||
|
unagreedPoliciesAndServicePairs.push({service, policies: unagreedPolicies});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there's anything left to agree to, prompt the user
|
||||||
|
if (unagreedPoliciesAndServicePairs.length > 0) {
|
||||||
|
const newlyAgreedUrls = await interactionCallback(unagreedPoliciesAndServicePairs, Array.from(agreedUrlSet));
|
||||||
|
console.log("User has agreed to URLs", newlyAgreedUrls);
|
||||||
|
agreedUrlSet = new Set(newlyAgreedUrls);
|
||||||
|
} else {
|
||||||
|
console.log("User has already agreed to all required policies");
|
||||||
|
}
|
||||||
|
|
||||||
|
const newAcceptedTerms = { accepted: Array.from(agreedUrlSet) };
|
||||||
|
await MatrixClientPeg.get().setAccountData('m.accepted_terms', newAcceptedTerms);
|
||||||
|
|
||||||
const agreePromises = policiesAndServicePairs.map((policiesAndService) => {
|
const agreePromises = policiesAndServicePairs.map((policiesAndService) => {
|
||||||
// filter the agreed URL list for ones that are actually for this service
|
// filter the agreed URL list for ones that are actually for this service
|
||||||
// (one URL may be used for multiple services)
|
// (one URL may be used for multiple services)
|
||||||
// Not a particularly efficient loop but probably fine given the numbers involved
|
// Not a particularly efficient loop but probably fine given the numbers involved
|
||||||
const urlsForService = agreedUrls.filter((url) => {
|
const urlsForService = Array.from(agreedUrlSet).filter((url) => {
|
||||||
for (const policy of Object.values(policiesAndService.policies)) {
|
for (const policy of Object.values(policiesAndService.policies)) {
|
||||||
for (const lang of Object.keys(policy)) {
|
for (const lang of Object.keys(policy)) {
|
||||||
if (lang === 'version') continue;
|
if (lang === 'version') continue;
|
||||||
|
@ -115,13 +160,14 @@ export async function startTermsFlow(services, interactionCallback) {
|
||||||
return Promise.all(agreePromises);
|
return Promise.all(agreePromises);
|
||||||
}
|
}
|
||||||
|
|
||||||
function dialogTermsInteractionCallback(policiesAndServicePairs) {
|
function dialogTermsInteractionCallback(policiesAndServicePairs, agreedUrls) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
console.log("Terms that need agreement", policiesAndServicePairs);
|
console.log("Terms that need agreement", policiesAndServicePairs);
|
||||||
const TermsDialog = sdk.getComponent("views.dialogs.TermsDialog");
|
const TermsDialog = sdk.getComponent("views.dialogs.TermsDialog");
|
||||||
|
|
||||||
Modal.createTrackedDialog('Terms of Service', '', TermsDialog, {
|
Modal.createTrackedDialog('Terms of Service', '', TermsDialog, {
|
||||||
policiesAndServicePairs,
|
policiesAndServicePairs,
|
||||||
|
agreedUrls,
|
||||||
onFinished: (done, agreedUrls) => {
|
onFinished: (done, agreedUrls) => {
|
||||||
if (!done) {
|
if (!done) {
|
||||||
reject(new TermsNotSignedError());
|
reject(new TermsNotSignedError());
|
||||||
|
|
|
@ -47,7 +47,12 @@ export default class TermsDialog extends React.PureComponent {
|
||||||
* Array of [Service, terms] pairs, where terms is the response from the
|
* Array of [Service, terms] pairs, where terms is the response from the
|
||||||
* /terms endpoint for that service
|
* /terms endpoint for that service
|
||||||
*/
|
*/
|
||||||
policiesAndServicePairs: PropTypes.arrayOf(PropTypes.object).isRequired,
|
policiesAndServicePairs: PropTypes.array.isRequired,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* urls that the user has already agreed to
|
||||||
|
*/
|
||||||
|
agreedUrls: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called with:
|
* Called with:
|
||||||
|
@ -57,12 +62,15 @@ export default class TermsDialog extends React.PureComponent {
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor(props) {
|
||||||
super();
|
super();
|
||||||
this.state = {
|
this.state = {
|
||||||
// url -> boolean
|
// url -> boolean
|
||||||
agreedUrls: {},
|
agreedUrls: {},
|
||||||
};
|
};
|
||||||
|
for (const url of props.agreedUrls) {
|
||||||
|
this.state.agreedUrls[url] = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onCancelClick = () => {
|
_onCancelClick = () => {
|
||||||
|
|
Loading…
Reference in a new issue