diff --git a/keyserver/src/responders/website-responders.js b/keyserver/src/responders/website-responders.js --- a/keyserver/src/responders/website-responders.js +++ b/keyserver/src/responders/website-responders.js @@ -341,6 +341,7 @@ deviceToken: undefined, dataLoaded: viewer.loggedIn, windowActive: true, + userPolicies: {}, _persist: null, }; diff --git a/lib/actions/user-actions.js b/lib/actions/user-actions.js --- a/lib/actions/user-actions.js +++ b/lib/actions/user-actions.js @@ -8,6 +8,7 @@ RegisterResult, RegisterInfo, UpdateUserSettingsRequest, + PolicyAcknowledgmentRequest, } from '../types/account-types'; import type { GetSessionPublicKeysArgs } from '../types/request-types'; import type { UserSearchResult } from '../types/search-types'; @@ -222,6 +223,19 @@ return await callServerEndpoint('get_session_public_keys', data); }; +const policyAcknowledgmentActionTypes = Object.freeze({ + started: 'POLICY_ACKNOWLEDGMENT_STARTED', + success: 'POLICY_ACKNOWLEDGMENT_SUCCESS', + failed: 'POLICY_ACKNOWLEDGMENT_FAILED', +}); +const policyAcknowledgment = ( + callServerEndpoint: CallServerEndpoint, +): (( + policyRequest: PolicyAcknowledgmentRequest, +) => Promise) => async policyRequest => { + await callServerEndpoint('policy_acknowledgment', policyRequest); +}; + export { changeUserPasswordActionTypes, changeUserPassword, @@ -240,4 +254,6 @@ setUserSettingsActionTypes, updateSubscription, updateSubscriptionActionTypes, + policyAcknowledgment, + policyAcknowledgmentActionTypes, }; diff --git a/lib/reducers/master-reducer.js b/lib/reducers/master-reducer.js --- a/lib/reducers/master-reducer.js +++ b/lib/reducers/master-reducer.js @@ -19,6 +19,7 @@ import reduceNextLocalID from './local-id-reducer'; import { reduceMessageStore } from './message-reducer'; import reduceBaseNavInfo from './nav-reducer'; +import policiesReducer from './policies-reducer.js'; import reduceReportStore from './report-store-reducer'; import { reduceThreadInfos } from './thread-reducer'; import reduceUpdatesCurrentAsOf from './updates-reducer'; @@ -105,6 +106,7 @@ ), nextLocalID: reduceNextLocalID(state.nextLocalID, action), dataLoaded: reduceDataLoaded(state.dataLoaded, action), + userPolicies: policiesReducer(state.userPolicies, action), }, storeOperations: { draftStoreOperations, diff --git a/lib/reducers/policies-reducer.js b/lib/reducers/policies-reducer.js new file mode 100644 --- /dev/null +++ b/lib/reducers/policies-reducer.js @@ -0,0 +1,40 @@ +// @flow + +import { policyAcknowledgmentActionTypes } from '../actions/user-actions.js'; +import type { PolicyType } from '../facts/policies.js'; +import { + type UserPolicies, + forcePolicyAcknowledgmentActionType, +} from '../types/policy-types.js'; +import type { BaseAction } from '../types/redux-types'; + +function policiesReducer( + state: UserPolicies, + action: BaseAction, +): UserPolicies { + if (action.type === forcePolicyAcknowledgmentActionType) { + const { notAcknowledgedPolicies } = action.payload; + const newState = { ...state }; + + notAcknowledgedPolicies.forEach((policy: PolicyType) => { + newState[policy] = { + ...newState[policy], + isAcknowledged: false, + }; + }); + return newState; + } + if (action.type === policyAcknowledgmentActionTypes.success) { + const { policy } = action.payload; + return { + ...state, + [policy]: { + ...state[policy], + isAcknowledged: true, + }, + }; + } + return state; +} + +export default policiesReducer; diff --git a/lib/types/policy-types.js b/lib/types/policy-types.js --- a/lib/types/policy-types.js +++ b/lib/types/policy-types.js @@ -6,3 +6,22 @@ +policy: PolicyType, +confirmed: boolean, }; + +export type UserPolicyState = { + +isAcknowledged: boolean, +}; + +export type UserPolicies = { + +[name: PolicyType]: UserPolicyState, +}; + +export type ForcePolicyAcknowledgmentPayload = { + +notAcknowledgedPolicies: $ReadOnlyArray, +}; + +export type PolicyAcknowledgmentPayload = { + +policy: PolicyType, +}; + +export const forcePolicyAcknowledgmentActionType = + 'FORCE_POLICY_ACKNOWLEDGMENT'; diff --git a/lib/types/redux-types.js b/lib/types/redux-types.js --- a/lib/types/redux-types.js +++ b/lib/types/redux-types.js @@ -49,6 +49,11 @@ } from './message-types'; import type { RawTextMessageInfo } from './messages/text'; import type { BaseNavInfo } from './nav-types'; +import { + type ForcePolicyAcknowledgmentPayload, + type PolicyAcknowledgmentPayload, + type UserPolicies, +} from './policy-types.js'; import type { RelationshipErrors } from './relationship-types'; import type { EnabledReports, @@ -97,6 +102,7 @@ reportStore: ReportStore, nextLocalID: number, dataLoaded: boolean, + userPolicies: UserPolicies, ... }; @@ -828,6 +834,27 @@ +error: true, +payload: Error, +loadingInfo: LoadingInfo, + } + | { + +type: 'FORCE_POLICY_ACKNOWLEDGMENT', + +payload: ForcePolicyAcknowledgmentPayload, + +loadingInfo: LoadingInfo, + } + | { + +type: 'POLICY_ACKNOWLEDGMENT_STARTED', + +payload?: void, + +loadingInfo: LoadingInfo, + } + | { + +type: 'POLICY_ACKNOWLEDGMENT_SUCCESS', + +payload: PolicyAcknowledgmentPayload, + +loadingInfo: LoadingInfo, + } + | { + +type: 'POLICY_ACKNOWLEDGMENT_FAILED', + +error: true, + +payload: Error, + +loadingInfo: LoadingInfo, }; export type ActionPayload = ?(Object | Array<*> | $ReadOnlyArray<*> | string); diff --git a/native/redux/redux-setup.js b/native/redux/redux-setup.js --- a/native/redux/redux-setup.js +++ b/native/redux/redux-setup.js @@ -130,6 +130,7 @@ deviceCameraInfo: defaultDeviceCameraInfo, deviceOrientation: Orientation.getInitialOrientation(), frozen: false, + userPolicies: {}, }: AppState); function reducer(state: AppState = defaultState, action: Action) { diff --git a/native/redux/state-types.js b/native/redux/state-types.js --- a/native/redux/state-types.js +++ b/native/redux/state-types.js @@ -10,6 +10,7 @@ import type { LifecycleState } from 'lib/types/lifecycle-state-types'; import type { LoadingStatus } from 'lib/types/loading-types'; import type { MessageStore } from 'lib/types/message-types'; +import type { UserPolicies } from 'lib/types/policy-types'; import type { ReportStore } from 'lib/types/report-types'; import type { ConnectionInfo } from 'lib/types/socket-types'; import type { ThreadStore } from 'lib/types/thread-types'; @@ -55,4 +56,5 @@ deviceCameraInfo: DeviceCameraInfo, deviceOrientation: Orientations, frozen: boolean, + userPolicies: UserPolicies, }; diff --git a/web/redux/redux-setup.js b/web/redux/redux-setup.js --- a/web/redux/redux-setup.js +++ b/web/redux/redux-setup.js @@ -19,6 +19,7 @@ import type { LifecycleState } from 'lib/types/lifecycle-state-types'; import type { LoadingStatus } from 'lib/types/loading-types'; import type { MessageStore } from 'lib/types/message-types'; +import type { UserPolicies } from 'lib/types/policy-types.js'; import type { BaseAction } from 'lib/types/redux-types'; import type { ReportStore } from 'lib/types/report-types'; import type { ConnectionInfo } from 'lib/types/socket-types'; @@ -67,6 +68,7 @@ userAgent: ?string, dataLoaded: boolean, windowActive: boolean, + userPolicies: UserPolicies, _persist: ?PersistState, };