diff --git a/keyserver/src/creators/role-creator.js b/keyserver/src/creators/role-creator.js --- a/keyserver/src/creators/role-creator.js +++ b/keyserver/src/creators/role-creator.js @@ -12,12 +12,19 @@ import type { RoleInfo, RoleModificationRequest, + RoleModificationResult, } from 'lib/types/thread-types.js'; +import { updateTypes } from 'lib/types/update-types-enum.js'; import { ServerError } from 'lib/utils/errors.js'; import createIDs from './id-creator.js'; +import { createUpdates } from './update-creator.js'; import { dbQuery, SQL } from '../database/database.js'; -import { fetchThreadInfos } from '../fetchers/thread-fetchers.js'; +import { + fetchThreadInfos, + fetchServerThreadInfos, + rawThreadInfosFromServerThreadInfos, +} from '../fetchers/thread-fetchers.js'; import { checkThreadPermission } from '../fetchers/thread-permission-fetchers.js'; import type { Viewer } from '../session/viewer.js'; @@ -76,7 +83,7 @@ async function modifyRole( viewer: Viewer, request: RoleModificationRequest, -): Promise { +): Promise { const hasPermission = await checkThreadPermission( viewer, request.community, @@ -138,6 +145,44 @@ } await dbQuery(query); + + const fetchServerThreadInfosResult = await fetchServerThreadInfos({ + threadID: community, + }); + const { threadInfos: serverThreadInfos } = fetchServerThreadInfosResult; + const serverThreadInfo = serverThreadInfos[community]; + + if (!serverThreadInfo) { + throw new ServerError('internal_error'); + } + + const updateDatas = []; + for (const memberInfo of serverThreadInfo.members) { + updateDatas.push({ + type: updateTypes.UPDATE_THREAD, + userID: memberInfo.id, + time, + threadID: community, + }); + } + + const { viewerUpdates } = await createUpdates(updateDatas, { + viewer, + updatesForCurrentSession: 'return', + }); + + const { threadInfos: rawThreadInfos } = rawThreadInfosFromServerThreadInfos( + viewer, + fetchServerThreadInfosResult, + ); + const rawThreadInfo = rawThreadInfos[community]; + + return { + threadInfo: rawThreadInfo, + updatesResult: { + newUpdates: viewerUpdates, + }, + }; } export { createInitialRolesForNewThread, modifyRole }; diff --git a/keyserver/src/responders/thread-responders.js b/keyserver/src/responders/thread-responders.js --- a/keyserver/src/responders/thread-responders.js +++ b/keyserver/src/responders/thread-responders.js @@ -27,6 +27,8 @@ type ToggleMessagePinRequest, type ToggleMessagePinResult, type RoleModificationRequest, + type RoleModificationResult, + rawThreadInfoValidator, } from 'lib/types/thread-types.js'; import { serverUpdateInfoValidator } from 'lib/types/update-types.js'; import { userInfosValidator } from 'lib/types/user-types.js'; @@ -356,16 +358,29 @@ action: t.enums.of(['create_role', 'edit_role']), }); +export const roleModificationResultValidator: TInterface = + tShape({ + threadInfo: t.maybe(rawThreadInfoValidator), + updatesResult: tShape({ + newUpdates: t.list(serverUpdateInfoValidator), + }), + }); + async function roleModificationResponder( viewer: Viewer, input: mixed, -): Promise { +): Promise { const request = await validateInput( viewer, roleModificationRequestInputValidator, input, ); - await modifyRole(viewer, request); + const response = await modifyRole(viewer, request); + return validateOutput( + viewer.platformDetails, + roleModificationResultValidator, + response, + ); } export { diff --git a/lib/actions/thread-actions.js b/lib/actions/thread-actions.js --- a/lib/actions/thread-actions.js +++ b/lib/actions/thread-actions.js @@ -15,6 +15,7 @@ ToggleMessagePinRequest, ToggleMessagePinResult, RoleModificationRequest, + RoleModificationPayload, } from '../types/thread-types.js'; import type { CallServerEndpoint } from '../utils/call-server-endpoint.js'; import { values } from '../utils/objects.js'; @@ -194,12 +195,21 @@ }; }; +const modifyCommunityRoleActionTypes = Object.freeze({ + started: 'MODIFY_COMMUNITY_ROLE_STARTED', + success: 'MODIFY_COMMUNITY_ROLE_SUCCESS', + failed: 'MODIFY_COMMUNITY_ROLE_FAILED', +}); const modifyCommunityRole = ( callServerEndpoint: CallServerEndpoint, - ): ((request: RoleModificationRequest) => Promise) => + ): ((request: RoleModificationRequest) => Promise) => async request => { - await callServerEndpoint('modify_community_role', request); + const response = await callServerEndpoint('modify_community_role', request); + return { + threadInfo: response.threadInfo, + updatesResult: response.updatesResult, + }; }; export { @@ -220,5 +230,6 @@ fetchThreadMedia, toggleMessagePinActionTypes, toggleMessagePin, + modifyCommunityRoleActionTypes, modifyCommunityRole, }; diff --git a/lib/reducers/thread-reducer.js b/lib/reducers/thread-reducer.js --- a/lib/reducers/thread-reducer.js +++ b/lib/reducers/thread-reducer.js @@ -17,6 +17,7 @@ changeThreadMemberRolesActionTypes, joinThreadActionTypes, leaveThreadActionTypes, + modifyCommunityRoleActionTypes, } from '../actions/thread-actions.js'; import { logOutActionTypes, @@ -223,7 +224,8 @@ action.type === changeThreadMemberRolesActionTypes.success || action.type === incrementalStateSyncActionType || action.type === processUpdatesActionType || - action.type === newThreadActionTypes.success + action.type === newThreadActionTypes.success || + action.type === modifyCommunityRoleActionTypes.success ) { const { newUpdates } = action.payload.updatesResult; if (newUpdates.length === 0) { 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 @@ -108,6 +108,7 @@ NewThreadResult, ThreadJoinPayload, ToggleMessagePinResult, + RoleModificationPayload, } from './thread-types.js'; import type { ClientUpdatesResultWithUserInfos } from './update-types.js'; import type { CurrentUserInfo, UserStore } from './user-types.js'; @@ -1168,7 +1169,23 @@ +type: 'UPDATE_LAST_COMMUNICATED_PLATFORM_DETAILS', +payload: LastCommunicatedPlatformDetails, } - | { +type: 'RESET_USER_STATE', +payload?: void }; + | { +type: 'RESET_USER_STATE', +payload?: void } + | { + +type: 'MODIFY_COMMUNITY_ROLE_STARTED', + +loadingInfo?: LoadingInfo, + +payload?: void, + } + | { + +type: 'MODIFY_COMMUNITY_ROLE_SUCCESS', + +payload: RoleModificationPayload, + +loadingInfo: LoadingInfo, + } + | { + +type: 'MODIFY_COMMUNITY_ROLE_FAILED', + +error: true, + +payload: Error, + +loadingInfo: LoadingInfo, + }; export type ActionPayload = ?(Object | Array<*> | $ReadOnlyArray<*> | string); export type SuperAction = { diff --git a/lib/types/thread-types.js b/lib/types/thread-types.js --- a/lib/types/thread-types.js +++ b/lib/types/thread-types.js @@ -404,6 +404,20 @@ +action: 'create_role' | 'edit_role', }; +export type RoleModificationResult = { + +threadInfo: RawThreadInfo, + +updatesResult: { + +newUpdates: $ReadOnlyArray, + }, +}; + +export type RoleModificationPayload = { + +threadInfo: RawThreadInfo, + +updatesResult: { + +newUpdates: $ReadOnlyArray, + }, +}; + // We can show a max of 3 sidebars inline underneath their parent in the chat // tab. If there are more, we show a button that opens a modal to see the rest export const maxReadSidebars = 3; diff --git a/native/roles/create-roles-header-right-button.react.js b/native/roles/create-roles-header-right-button.react.js --- a/native/roles/create-roles-header-right-button.react.js +++ b/native/roles/create-roles-header-right-button.react.js @@ -4,8 +4,14 @@ import * as React from 'react'; import { TouchableOpacity, Text } from 'react-native'; -import { modifyCommunityRole } from 'lib/actions/thread-actions.js'; -import { useServerCall } from 'lib/utils/action-utils.js'; +import { + modifyCommunityRole, + modifyCommunityRoleActionTypes, +} from 'lib/actions/thread-actions.js'; +import { + useServerCall, + useDispatchActionPromise, +} from 'lib/utils/action-utils.js'; import type { NavigationRoute } from '../navigation/route-names'; import { useStyles } from '../themes/colors.js'; @@ -20,18 +26,23 @@ const styles = useStyles(unboundStyles); const callModifyCommunityRole = useServerCall(modifyCommunityRole); + const dispatchActionPromise = useDispatchActionPromise(); const onPressCreate = React.useCallback(() => { - callModifyCommunityRole({ - community: threadInfo.id, - action, - name: roleName, - permissions: [...rolePermissions], - }); + dispatchActionPromise( + modifyCommunityRoleActionTypes, + callModifyCommunityRole({ + community: threadInfo.id, + action, + name: roleName, + permissions: [...rolePermissions], + }), + ); navigation.goBack(); }, [ callModifyCommunityRole, + dispatchActionPromise, threadInfo, action, roleName,