Page MenuHomePhabricator

D7211.diff
No OneTemporary

D7211.diff

diff --git a/keyserver/src/fetchers/thread-fetchers.js b/keyserver/src/fetchers/thread-fetchers.js
--- a/keyserver/src/fetchers/thread-fetchers.js
+++ b/keyserver/src/fetchers/thread-fetchers.js
@@ -36,12 +36,13 @@
`.append(whereClause);
const threadsQuery = SQL`
- SELECT t.id, t.name, t.parent_thread_id, t.containing_thread_id,
- t.community, t.depth, t.color, t.description, t.type, t.creation_time,
- t.source_message, t.replies_count, t.avatar, m.user, m.role, m.permissions,
- m.subscription, m.last_read_message < m.last_message AS unread, m.sender
- FROM threads t
- LEFT JOIN memberships m ON m.thread = t.id AND m.role >= 0
+ SELECT t.id, t.name, t.parent_thread_id, t.containing_thread_id,
+ t.community, t.depth, t.color, t.description, t.type, t.creation_time,
+ t.source_message, t.replies_count, t.avatar, t.pinned_count, m.user,
+ m.role, m.permissions, m.subscription,
+ m.last_read_message < m.last_message AS unread, m.sender
+ FROM threads t
+ LEFT JOIN memberships m ON m.thread = t.id AND m.role >= 0
`
.append(whereClause)
.append(SQL` ORDER BY m.user ASC`);
@@ -74,6 +75,7 @@
members: [],
roles: {},
repliesCount: threadsRow.replies_count,
+ pinnedCount: threadsRow.pinned_count,
};
if (threadsRow.avatar) {
threadInfos[threadID] = {
@@ -165,6 +167,10 @@
// native release with thread avatar editing enabled.
const filterThreadEditAvatarPermission = true;
+ const hasCodeVersionBelow209 = !hasMinCodeVersion(
+ viewer.platformDetails,
+ 209,
+ );
const threadInfos = {};
for (const threadID in serverResult.threadInfos) {
const serverThreadInfo = serverResult.threadInfos[threadID];
@@ -178,6 +184,7 @@
hideThreadStructure: hasCodeVersionBelow102,
filterDetailedThreadEditPermissions: hasCodeVersionBelow104,
filterThreadEditAvatarPermission,
+ excludePinInfo: hasCodeVersionBelow209,
},
);
if (threadInfo) {
diff --git a/lib/shared/thread-utils.js b/lib/shared/thread-utils.js
--- a/lib/shared/thread-utils.js
+++ b/lib/shared/thread-utils.js
@@ -377,6 +377,7 @@
},
repliesCount: 0,
sourceMessageID,
+ pinnedCount: 0,
};
const userInfos = {};
@@ -701,6 +702,7 @@
},
+filterDetailedThreadEditPermissions?: boolean,
+filterThreadEditAvatarPermission?: boolean,
+ +excludePinInfo?: boolean,
};
function rawThreadInfoFromServerThreadInfo(
serverThreadInfo: ServerThreadInfo,
@@ -715,6 +717,7 @@
options?.filterDetailedThreadEditPermissions;
const filterThreadEditAvatarPermission =
options?.filterThreadEditAvatarPermission;
+ const excludePinInfo = options?.excludePinInfo;
const filterThreadPermissions = _omitBy(
(v, k) =>
@@ -728,6 +731,12 @@
threadPermissions.EDIT_THREAD_AVATAR,
threadPermissionPropagationPrefixes.DESCENDANT +
threadPermissions.EDIT_THREAD_AVATAR,
+ ].includes(k)) ||
+ (excludePinInfo &&
+ [
+ threadPermissions.MANAGE_PINS,
+ threadPermissionPropagationPrefixes.DESCENDANT +
+ threadPermissions.MANAGE_PINS,
].includes(k)),
);
@@ -836,6 +845,12 @@
visibilityRules: rawThreadInfo.type,
};
}
+ if (!excludePinInfo) {
+ return {
+ ...rawThreadInfo,
+ pinnedCount: serverThreadInfo.pinnedCount,
+ };
+ }
return rawThreadInfo;
}
@@ -883,7 +898,7 @@
...threadInfo,
uiName: threadUIName(threadInfo),
};
- const { sourceMessageID, avatar } = rawThreadInfo;
+ const { sourceMessageID, avatar, pinnedCount } = rawThreadInfo;
if (sourceMessageID) {
threadInfo = { ...threadInfo, sourceMessageID };
}
@@ -899,6 +914,11 @@
avatar: getUserAvatarForThread(rawThreadInfo, viewerID, userInfos),
};
}
+
+ if (pinnedCount) {
+ threadInfo = { ...threadInfo, pinnedCount };
+ }
+
return threadInfo;
}
diff --git a/lib/shared/unshim-utils.js b/lib/shared/unshim-utils.js
--- a/lib/shared/unshim-utils.js
+++ b/lib/shared/unshim-utils.js
@@ -44,6 +44,7 @@
messageTypes.SIDEBAR_SOURCE,
messageTypes.MULTIMEDIA,
messageTypes.REACTION,
+ messageTypes.TOGGLE_PIN,
]);
function unshimMessageInfos(
messageInfos: $ReadOnlyArray<RawMessageInfo>,
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
@@ -217,6 +217,7 @@
+currentUser: ThreadCurrentUserInfo,
+sourceMessageID?: string,
+repliesCount: number,
+ +pinnedCount?: number,
};
export type ThreadInfo = {
@@ -236,6 +237,7 @@
+currentUser: ThreadCurrentUserInfo,
+sourceMessageID?: string,
+repliesCount: number,
+ +pinnedCount?: number,
};
export type ResolvedThreadInfo = {
@@ -255,6 +257,7 @@
+currentUser: ThreadCurrentUserInfo,
+sourceMessageID?: string,
+repliesCount: number,
+ +pinnedCount?: number,
};
export type ServerMemberInfo = {
@@ -282,6 +285,7 @@
+roles: { [id: string]: RoleInfo },
+sourceMessageID?: string,
+repliesCount: number,
+ +pinnedCount: number,
};
export type ThreadStore = {
@@ -323,6 +327,7 @@
+currentUser: string,
+sourceMessageID?: string,
+repliesCount: number,
+ +pinnedCount?: number,
};
export type ClientDBReplaceThreadOperation = {
diff --git a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp
--- a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp
+++ b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp
@@ -255,6 +255,7 @@
? jsi::String::createFromUtf8(rt, *thread.source_message_id)
: jsi::Value::null());
jsiThread.setProperty(rt, "repliesCount", thread.replies_count);
+ jsiThread.setProperty(rt, "pinnedCount", thread.pinned_count);
if (thread.avatar) {
auto avatar = jsi::String::createFromUtf8(rt, *thread.avatar);
@@ -643,6 +644,10 @@
? std::make_unique<std::string>(maybeAvatar.asString(rt).utf8(rt))
: nullptr;
+ jsi::Value maybePinnedCount = threadObj.getProperty(rt, "pinnedCount");
+ int pinnedCount = maybePinnedCount.isNumber()
+ ? std::lround(maybePinnedCount.asNumber())
+ : 0;
Thread thread{
threadID,
type,
@@ -658,7 +663,8 @@
currentUser,
std::move(sourceMessageID),
repliesCount,
- std::move(avatar)};
+ std::move(avatar),
+ pinnedCount};
threadStoreOps.push_back(
std::make_unique<ReplaceThreadOperation>(std::move(thread)));
diff --git a/native/redux/manage-pins-permission-migration.js b/native/redux/manage-pins-permission-migration.js
new file mode 100644
--- /dev/null
+++ b/native/redux/manage-pins-permission-migration.js
@@ -0,0 +1,89 @@
+// @flow
+
+import type {
+ RawThreadInfo,
+ MemberInfo,
+ ThreadCurrentUserInfo,
+ RoleInfo,
+} from 'lib/types/thread-types.js';
+
+type ThreadStoreThreadInfos = { +[id: string]: RawThreadInfo };
+type TargetMemberInfo = MemberInfo | ThreadCurrentUserInfo;
+
+const adminRoleName = 'Admins';
+
+function addManagePinsThreadPermissionToUser(
+ threadInfo: RawThreadInfo,
+ member: TargetMemberInfo,
+ threadID: string,
+): TargetMemberInfo {
+ const isAdmin =
+ member.role && threadInfo.roles[member.role].name === adminRoleName;
+ let newPermissionsForMember;
+ if (isAdmin) {
+ newPermissionsForMember = {
+ ...member.permissions,
+ manage_pins: { value: true, source: threadID },
+ };
+ }
+
+ return newPermissionsForMember
+ ? {
+ ...member,
+ permissions: newPermissionsForMember,
+ }
+ : member;
+}
+
+function addManagePinsThreadPermissionToRole(role: RoleInfo): RoleInfo {
+ const isAdminRole = role.name === adminRoleName;
+ let updatedPermissions;
+
+ if (isAdminRole) {
+ updatedPermissions = {
+ ...role.permissions,
+ manage_pins: true,
+ descendant_manage_pins: true,
+ };
+ }
+
+ return updatedPermissions
+ ? { ...role, permissions: updatedPermissions }
+ : role;
+}
+
+function persistMigrationForManagePinsThreadPermission(
+ threadInfos: ThreadStoreThreadInfos,
+): ThreadStoreThreadInfos {
+ const newThreadInfos = {};
+ for (const threadID in threadInfos) {
+ const threadInfo: RawThreadInfo = threadInfos[threadID];
+ const updatedMembers = threadInfo.members.map(member =>
+ addManagePinsThreadPermissionToUser(threadInfo, member, threadID),
+ );
+
+ const updatedCurrentUser = addManagePinsThreadPermissionToUser(
+ threadInfo,
+ threadInfo.currentUser,
+ threadID,
+ );
+
+ const updatedRoles = {};
+ for (const roleID in threadInfo.roles) {
+ updatedRoles[roleID] = addManagePinsThreadPermissionToRole(
+ threadInfo.roles[roleID],
+ );
+ }
+
+ const updatedThreadInfo = {
+ ...threadInfo,
+ members: updatedMembers,
+ currentUser: updatedCurrentUser,
+ roles: updatedRoles,
+ };
+ newThreadInfos[threadID] = updatedThreadInfo;
+ }
+ return newThreadInfos;
+}
+
+export { persistMigrationForManagePinsThreadPermission };
diff --git a/native/redux/persist.js b/native/redux/persist.js
--- a/native/redux/persist.js
+++ b/native/redux/persist.js
@@ -13,7 +13,10 @@
getContainingThreadID,
getCommunity,
} from 'lib/shared/thread-utils.js';
-import { DEPRECATED_unshimMessageStore } from 'lib/shared/unshim-utils.js';
+import {
+ DEPRECATED_unshimMessageStore,
+ unshimFunc,
+} from 'lib/shared/unshim-utils.js';
import { defaultEnabledApps } from 'lib/types/enabled-apps.js';
import { defaultCalendarFilters } from 'lib/types/filter-types.js';
import {
@@ -23,11 +26,23 @@
type ClientDBMessageStoreOperation,
} from 'lib/types/message-types.js';
import { defaultConnectionInfo } from 'lib/types/socket-types.js';
-import { translateRawMessageInfoToClientDBMessageInfo } from 'lib/utils/message-ops-utils.js';
+import type {
+ ClientDBThreadStoreOperation,
+ ClientDBThreadInfo,
+} from 'lib/types/thread-types.js';
+import {
+ translateClientDBMessageInfoToRawMessageInfo,
+ translateRawMessageInfoToClientDBMessageInfo,
+} from 'lib/utils/message-ops-utils.js';
import { defaultNotifPermissionAlertInfo } from 'lib/utils/push-alerts.js';
-import { convertThreadStoreOperationsToClientDBOperations } from 'lib/utils/thread-ops-utils.js';
+import {
+ convertClientDBThreadInfoToRawThreadInfo,
+ convertRawThreadInfoToClientDBThreadInfo,
+ convertThreadStoreOperationsToClientDBOperations,
+} from 'lib/utils/thread-ops-utils.js';
import { migrateThreadStoreForEditThreadPermissions } from './edit-thread-permission-migration.js';
+import { persistMigrationForManagePinsThreadPermission } from './manage-pins-permission-migration.js';
import type { AppState } from './state-types.js';
import { unshimClientDB } from './unshim-utils.js';
import { commCoreModule } from '../native-modules.js';
@@ -391,6 +406,104 @@
return stateSansThreadIDsToNotifIDs;
},
[35]: (state: AppState) => unshimClientDB(state, [messageTypes.MULTIMEDIA]),
+ [36]: (state: AppState) => {
+ // 1. Get threads and messages from SQLite `threads` and `messages` tables.
+ const clientDBThreadInfos = commCoreModule.getAllThreadsSync();
+ const clientDBMessageInfos = commCoreModule.getAllMessagesSync();
+
+ // 2. Translate `ClientDBThreadInfo`s to `RawThreadInfo`s and
+ // `ClientDBMessageInfo`s to `RawMessageInfo`s.
+ const rawThreadInfos = clientDBThreadInfos.map(
+ convertClientDBThreadInfoToRawThreadInfo,
+ );
+ const rawMessageInfos = clientDBMessageInfos.map(
+ translateClientDBMessageInfoToRawMessageInfo,
+ );
+
+ // 3. Unshim translated `RawMessageInfos` to get the TOGGLE_PIN messages
+ const unshimmedRawMessageInfos = rawMessageInfos.map(messageInfo =>
+ unshimFunc(messageInfo, new Set([messageTypes.TOGGLE_PIN])),
+ );
+
+ // 4. Filter out non-TOGGLE_PIN messages
+ const filteredRawMessageInfos = unshimmedRawMessageInfos.filter(
+ messageInfo => messageInfo.type === messageTypes.TOGGLE_PIN,
+ );
+
+ // 5. We want only the last TOGGLE_PIN message for each message ID,
+ // so 'pin', 'unpin', 'pin' don't count as 3 pins, but only 1.
+ const lastMessageIDToRawMessageInfoMap = new Map();
+ for (const messageInfo of filteredRawMessageInfos) {
+ const { targetMessageID } = messageInfo;
+ lastMessageIDToRawMessageInfoMap.set(targetMessageID, messageInfo);
+ }
+ const lastMessageIDToRawMessageInfos = Array.from(
+ lastMessageIDToRawMessageInfoMap.values(),
+ );
+
+ // 6. Create a Map of threadIDs to pinnedCount
+ const threadIDsToPinnedCount = new Map();
+ for (const messageInfo of lastMessageIDToRawMessageInfos) {
+ const { threadID, type } = messageInfo;
+ if (type === messageTypes.TOGGLE_PIN) {
+ const pinnedCount = threadIDsToPinnedCount.get(threadID) || 0;
+ threadIDsToPinnedCount.set(threadID, pinnedCount + 1);
+ }
+ }
+
+ // 7. Include a pinnedCount for each rawThreadInfo
+ const rawThreadInfosWithPinnedCount = rawThreadInfos.map(threadInfo => ({
+ ...threadInfo,
+ pinnedCount: threadIDsToPinnedCount.get(threadInfo.id) || 0,
+ }));
+
+ // 8. Convert rawThreadInfos to a map of threadID to threadInfo
+ const threadIDToThreadInfo = rawThreadInfosWithPinnedCount.reduce(
+ (acc, threadInfo) => {
+ acc[threadInfo.id] = threadInfo;
+ return acc;
+ },
+ {},
+ );
+
+ // 9. Add threadPermission to each threadInfo
+ const rawThreadInfosWithThreadPermission =
+ persistMigrationForManagePinsThreadPermission(threadIDToThreadInfo);
+
+ // 10. Convert the new threadInfos back into an array
+ const rawThreadInfosWithCountAndPermission = Object.keys(
+ rawThreadInfosWithThreadPermission,
+ ).map(id => rawThreadInfosWithThreadPermission[id]);
+
+ // 11. Translate `RawThreadInfo`s to `ClientDBThreadInfo`s.
+ const convertedClientDBThreadInfos =
+ rawThreadInfosWithCountAndPermission.map(
+ convertRawThreadInfoToClientDBThreadInfo,
+ );
+
+ // 12. Construct `ClientDBThreadStoreOperation`s to clear SQLite `threads`
+ // table and repopulate with `ClientDBThreadInfo`s.
+ const operations: $ReadOnlyArray<ClientDBThreadStoreOperation> = [
+ {
+ type: 'remove_all',
+ },
+ ...convertedClientDBThreadInfos.map((thread: ClientDBThreadInfo) => ({
+ type: 'replace',
+ payload: thread,
+ })),
+ ];
+
+ // 13. Try processing `ClientDBThreadStoreOperation`s and log out if
+ // `processThreadStoreOperationsSync(...)` throws an exception.
+ try {
+ commCoreModule.processThreadStoreOperationsSync(operations);
+ } catch (exception) {
+ console.log(exception);
+ return { ...state, cookie: null };
+ }
+
+ return state;
+ },
};
// After migration 31, we'll no longer want to persist `messageStore.messages`
@@ -471,7 +584,7 @@
'storeLoaded',
],
debug: __DEV__,
- version: 35,
+ version: 36,
transforms: [messageStoreMessagesBlocklistTransform],
migrate: (createMigrate(migrations, { debug: __DEV__ }): any),
timeout: ((__DEV__ ? 0 : undefined): number | void),

File Metadata

Mime Type
text/plain
Expires
Sun, Nov 24, 9:29 PM (21 h, 45 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2577369
Default Alt Text
D7211.diff (15 KB)

Event Timeline