forked from Ivasoft/mattermost-mobile
Compare commits
47 Commits
release-2.
...
release-2.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7e29b2de8 | ||
|
|
57bd711836 | ||
|
|
b9b2991861 | ||
|
|
7927d9ce78 | ||
|
|
f724e0abfa | ||
|
|
788be8478d | ||
|
|
bbc3d5ea82 | ||
|
|
2e87df6a8d | ||
|
|
89be2be00e | ||
|
|
75c56a993f | ||
|
|
8d267f320d | ||
|
|
48b5b6099c | ||
|
|
9b4c31bacf | ||
|
|
e6bb2b826f | ||
|
|
2cec2f02f0 | ||
|
|
cf2a29e219 | ||
|
|
26801c2516 | ||
|
|
e876942892 | ||
|
|
210642f287 | ||
|
|
e635d04505 | ||
|
|
245f89815e | ||
|
|
ec99c8bc0d | ||
|
|
0a1c1a8bf7 | ||
|
|
ec2aeca0d0 | ||
|
|
cfb09ce7d7 | ||
|
|
ee7b4f05d5 | ||
|
|
069eaa9f52 | ||
|
|
4764a76c9f | ||
|
|
70119fc026 | ||
|
|
0d9c6e0ad3 | ||
|
|
f7d8ed9e1f | ||
|
|
b8cc13d7fa | ||
|
|
317568b4c8 | ||
|
|
55f919dd27 | ||
|
|
da1b3dc71d | ||
|
|
57a9ff31bf | ||
|
|
4f86a87bdc | ||
|
|
9ab21b2f62 | ||
|
|
1934945d72 | ||
|
|
5162e6b6e7 | ||
|
|
9139a26967 | ||
|
|
56fbb3d842 | ||
|
|
56349f865f | ||
|
|
fdf593bcec | ||
|
|
8e2e016a6c | ||
|
|
4d9bc1fbed | ||
|
|
7351c7ccac |
@@ -114,7 +114,6 @@ commands:
|
||||
command: |
|
||||
NODE_ENV=development npm ci --ignore-scripts
|
||||
node node_modules/\@sentry/cli/scripts/install.js
|
||||
node node_modules/react-native-webrtc/tools/downloadWebRTC.js
|
||||
- save_cache:
|
||||
name: Save npm cache
|
||||
key: v2-npm-{{ checksum "package.json" }}-{{ arch }}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Submit feature requests to https://portal.productboard.com/mattermost/33-what-matters-to-you. File non-security related bugs here in the following format:
|
||||
Submit feature requests to http://www.mattermost.org/feature-requests/. File non-security related bugs here in the following format:
|
||||
|
||||
#### Summary
|
||||
Issue in one concise sentence.
|
||||
|
||||
65
NOTICE.txt
65
NOTICE.txt
@@ -91,39 +91,6 @@ A spec-compliant polyfill for Intl.RelativeTimeFormat fully tested by the offici
|
||||
|
||||
---
|
||||
|
||||
## @gorhom/bottom-sheet
|
||||
|
||||
This product contains '@gorhom/bottom-sheet' by Mo Gorhom.
|
||||
|
||||
A performant interactive bottom sheet with fully configurable options
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/gorhom/react-native-bottom-sheet
|
||||
|
||||
* LICENSE: MIT License
|
||||
|
||||
Copyright (c) 2020 Mo Gorhom
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## @mattermost/compass-icons
|
||||
|
||||
This product contains '@mattermost/compass-icons' by Mattermost.
|
||||
@@ -2824,38 +2791,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## react-native-walkthrough-tooltip
|
||||
|
||||
This product contains 'react-native-walkthrough-tooltip' by Jason Gaare.
|
||||
|
||||
React Native Walkthrough Tooltip is a fullscreen modal that highlights whichever element it wraps.
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/jasongaare/react-native-walkthrough-tooltip
|
||||
|
||||
* LICENSE: MIT License
|
||||
|
||||
Copyright (c) 2018 Jason Gaare
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -112,8 +112,8 @@ android {
|
||||
applicationId "com.mattermost.rnbeta"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 457
|
||||
versionName "2.1.0"
|
||||
versionCode 456
|
||||
versionName "2.0.1"
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export async function storeCategories(serverUrl: string, categories: CategoryWit
|
||||
}
|
||||
|
||||
if (models.length > 0) {
|
||||
await operator.batchRecords(models, 'storeCategories');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
return {models};
|
||||
@@ -103,7 +103,7 @@ export async function addChannelToDefaultCategory(serverUrl: string, channel: Ch
|
||||
const models = await prepareCategoryChannels(operator, categoriesWithChannels);
|
||||
|
||||
if (models.length && !prepareRecordsOnly) {
|
||||
await operator.batchRecords(models, 'addChannelToDefaultCategory');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
return {models};
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
prepareDeleteChannel, prepareMyChannelsForTeam, queryAllMyChannel,
|
||||
getMyChannel, getChannelById, queryUsersOnChannel, queryUserChannelsByTypes,
|
||||
} from '@queries/servers/channel';
|
||||
import {queryDisplayNamePreferences} from '@queries/servers/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {prepareCommonSystemValues, PrepareCommonSystemValuesArgs, getCommonSystemValues, getCurrentTeamId, setCurrentChannelId, getCurrentUserId, getConfig, getLicense} from '@queries/servers/system';
|
||||
import {addChannelToTeamHistory, addTeamToTeamHistory, getTeamById, removeChannelFromTeamHistory} from '@queries/servers/team';
|
||||
import {getCurrentUser, queryUsersById} from '@queries/servers/user';
|
||||
@@ -82,7 +82,7 @@ export async function switchToChannel(serverUrl: string, channelId: string, team
|
||||
}
|
||||
|
||||
if (models.length && !prepareRecordsOnly) {
|
||||
await operator.batchRecords(models, 'switchToChannel');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
if (isTabletDevice) {
|
||||
@@ -124,7 +124,7 @@ export async function removeCurrentUserFromChannel(serverUrl: string, channelId:
|
||||
await removeChannelFromTeamHistory(operator, teamId, channel.id, false);
|
||||
|
||||
if (models.length && !prepareRecordsOnly) {
|
||||
await operator.batchRecords(models, 'removeCurrentUserFromChannel');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
}
|
||||
return {models};
|
||||
@@ -145,7 +145,7 @@ export async function setChannelDeleteAt(serverUrl: string, channelId: string, d
|
||||
const model = channel.prepareUpdate((c) => {
|
||||
c.deleteAt = deleteAt;
|
||||
});
|
||||
await operator.batchRecords([model], 'setChannelDeleteAt');
|
||||
await operator.batchRecords([model]);
|
||||
} catch (error) {
|
||||
logError('FAILED TO BATCH CHANGES FOR CHANNEL DELETE AT', error);
|
||||
}
|
||||
@@ -179,7 +179,7 @@ export async function markChannelAsViewed(serverUrl: string, channelId: string,
|
||||
});
|
||||
PushNotifications.removeChannelNotifications(serverUrl, channelId);
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([member], 'markChannelAsViewed');
|
||||
await operator.batchRecords([member]);
|
||||
}
|
||||
|
||||
return {member};
|
||||
@@ -206,7 +206,7 @@ export async function markChannelAsUnread(serverUrl: string, channelId: string,
|
||||
m.isUnread = true;
|
||||
});
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([member], 'markChannelAsUnread');
|
||||
await operator.batchRecords([member]);
|
||||
}
|
||||
|
||||
return {member};
|
||||
@@ -226,7 +226,7 @@ export async function resetMessageCount(serverUrl: string, channelId: string) {
|
||||
member.prepareUpdate((m) => {
|
||||
m.messageCount = 0;
|
||||
});
|
||||
await operator.batchRecords([member], 'resetMessageCount');
|
||||
await operator.batchRecords([member]);
|
||||
|
||||
return member;
|
||||
} catch (error) {
|
||||
@@ -254,7 +254,7 @@ export async function storeMyChannelsForTeam(serverUrl: string, teamId: string,
|
||||
}
|
||||
|
||||
if (flattenedModels.length) {
|
||||
await operator.batchRecords(flattenedModels, 'storeMyChannelsForTeam');
|
||||
await operator.batchRecords(flattenedModels);
|
||||
}
|
||||
|
||||
return {models: flattenedModels};
|
||||
@@ -273,7 +273,7 @@ export async function updateMyChannelFromWebsocket(serverUrl: string, channelMem
|
||||
m.roles = channelMember.roles;
|
||||
});
|
||||
if (!prepareRecordsOnly) {
|
||||
operator.batchRecords([member], 'updateMyChannelFromWebsocket');
|
||||
operator.batchRecords([member]);
|
||||
}
|
||||
}
|
||||
return {model: member};
|
||||
@@ -293,7 +293,7 @@ export async function updateChannelInfoFromChannel(serverUrl: string, channel: C
|
||||
}],
|
||||
prepareRecordsOnly: true});
|
||||
if (!prepareRecordsOnly) {
|
||||
operator.batchRecords(newInfo, 'updateChannelInfoFromChannel');
|
||||
operator.batchRecords(newInfo);
|
||||
}
|
||||
return {model: newInfo};
|
||||
} catch (error) {
|
||||
@@ -317,7 +317,7 @@ export async function updateLastPostAt(serverUrl: string, channelId: string, las
|
||||
});
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([myChannel], 'updateLastPostAt');
|
||||
await operator.batchRecords([myChannel]);
|
||||
}
|
||||
|
||||
return {member: myChannel};
|
||||
@@ -345,7 +345,7 @@ export async function updateMyChannelLastFetchedAt(serverUrl: string, channelId:
|
||||
});
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([myChannel], 'updateMyChannelLastFetchedAt');
|
||||
await operator.batchRecords([myChannel]);
|
||||
}
|
||||
|
||||
return {member: myChannel};
|
||||
@@ -369,7 +369,7 @@ export async function updateChannelsDisplayName(serverUrl: string, channels: Cha
|
||||
|
||||
const license = await getLicense(database);
|
||||
const config = await getConfig(database);
|
||||
const preferences = await queryDisplayNamePreferences(database, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const preferences = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const displaySettings = getTeammateNameDisplaySetting(preferences, config.LockTeammateNameDisplay, config.TeammateNameDisplay, license);
|
||||
const models: Model[] = [];
|
||||
for await (const channel of channels) {
|
||||
@@ -403,7 +403,7 @@ export async function updateChannelsDisplayName(serverUrl: string, channels: Cha
|
||||
}
|
||||
|
||||
if (models.length && !prepareRecordsOnly) {
|
||||
await operator.batchRecords(models, 'updateChannelsDisplayName');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
return {models};
|
||||
|
||||
@@ -26,7 +26,7 @@ export async function updateDraftFile(serverUrl: string, channelId: string, root
|
||||
});
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([draft], 'updateDraftFile');
|
||||
await operator.batchRecords([draft]);
|
||||
}
|
||||
|
||||
return {draft};
|
||||
@@ -58,7 +58,7 @@ export async function removeDraftFile(serverUrl: string, channelId: string, root
|
||||
}
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([draft], 'removeDraftFile');
|
||||
await operator.batchRecords([draft]);
|
||||
}
|
||||
|
||||
return {draft};
|
||||
@@ -99,7 +99,7 @@ export async function updateDraftMessage(serverUrl: string, channelId: string, r
|
||||
}
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([draft], 'updateDraftMessage');
|
||||
await operator.batchRecords([draft]);
|
||||
}
|
||||
|
||||
return {draft};
|
||||
@@ -129,7 +129,7 @@ export async function addFilesToDraft(serverUrl: string, channelId: string, root
|
||||
});
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([draft], 'addFilesToDraft');
|
||||
await operator.batchRecords([draft]);
|
||||
}
|
||||
|
||||
return {draft};
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
import {fetchPostAuthors} from '@actions/remote/post';
|
||||
import {ActionType, Post} from '@constants';
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {getPostById, prepareDeletePost, queryPostsById} from '@queries/servers/post';
|
||||
import {getCurrentUserId} from '@queries/servers/system';
|
||||
@@ -19,8 +18,6 @@ import type MyChannelModel from '@typings/database/models/servers/my_channel';
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
|
||||
const {SERVER: {DRAFT, FILE, POST, POSTS_IN_THREAD, REACTION, THREAD, THREAD_PARTICIPANT, THREADS_IN_TEAM}} = MM_TABLES;
|
||||
|
||||
export const sendAddToChannelEphemeralPost = async (serverUrl: string, user: UserModel, addedUsernames: string[], messages: string[], channeId: string, postRootId = '') => {
|
||||
try {
|
||||
const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
@@ -127,14 +124,14 @@ export async function removePost(serverUrl: string, post: PostModel | Post) {
|
||||
}
|
||||
|
||||
if (removeModels.length) {
|
||||
await operator.batchRecords(removeModels, 'removePost (combined user activity)');
|
||||
await operator.batchRecords(removeModels);
|
||||
}
|
||||
} else {
|
||||
const postModel = await getPostById(database, post.id);
|
||||
if (postModel) {
|
||||
const preparedPost = await prepareDeletePost(postModel);
|
||||
if (preparedPost.length) {
|
||||
await operator.batchRecords(preparedPost, 'removePost');
|
||||
await operator.batchRecords(preparedPost);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,7 +159,7 @@ export async function markPostAsDeleted(serverUrl: string, post: Post, prepareRe
|
||||
});
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([dbPost], 'markPostAsDeleted');
|
||||
await operator.batchRecords([dbPost]);
|
||||
}
|
||||
return {model};
|
||||
} catch (error) {
|
||||
@@ -229,7 +226,7 @@ export async function storePostsForChannel(
|
||||
}
|
||||
|
||||
if (models.length && !prepareRecordsOnly) {
|
||||
await operator.batchRecords(models, 'storePostsForChannel');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
return {models};
|
||||
@@ -247,31 +244,3 @@ export async function getPosts(serverUrl: string, ids: string[]) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function deletePosts(serverUrl: string, postIds: string[]) {
|
||||
try {
|
||||
const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
|
||||
const postsFormatted = `'${postIds.join("','")}'`;
|
||||
|
||||
await database.write(() => {
|
||||
return database.adapter.unsafeExecute({
|
||||
sqls: [
|
||||
[`DELETE FROM ${POST} where id IN (${postsFormatted})`, []],
|
||||
[`DELETE FROM ${REACTION} where post_id IN (${postsFormatted})`, []],
|
||||
[`DELETE FROM ${FILE} where post_id IN (${postsFormatted})`, []],
|
||||
[`DELETE FROM ${DRAFT} where root_id IN (${postsFormatted})`, []],
|
||||
|
||||
[`DELETE FROM ${POSTS_IN_THREAD} where root_id IN (${postsFormatted})`, []],
|
||||
|
||||
[`DELETE FROM ${THREAD} where id IN (${postsFormatted})`, []],
|
||||
[`DELETE FROM ${THREAD_PARTICIPANT} where thread_id IN (${postsFormatted})`, []],
|
||||
[`DELETE FROM ${THREADS_IN_TEAM} where thread_id IN (${postsFormatted})`, []],
|
||||
],
|
||||
});
|
||||
});
|
||||
return {error: false};
|
||||
} catch (error) {
|
||||
return {error};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,14 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Q} from '@nozbe/watermelondb';
|
||||
import deepEqual from 'deep-equal';
|
||||
|
||||
import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database';
|
||||
import {SYSTEM_IDENTIFIERS} from '@constants/database';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {getServerCredentials} from '@init/credentials';
|
||||
import {queryAllChannelsForTeam} from '@queries/servers/channel';
|
||||
import {getConfig, getLicense, getGlobalDataRetentionPolicy, getGranularDataRetentionPolicies, getLastGlobalDataRetentionRun, getIsDataRetentionEnabled} from '@queries/servers/system';
|
||||
import {getConfig, getLicense} from '@queries/servers/system';
|
||||
import {logError} from '@utils/log';
|
||||
|
||||
import {deletePosts} from './post';
|
||||
|
||||
import type {DataRetentionPoliciesRequest} from '@actions/remote/systems';
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
|
||||
const {SERVER: {POST}} = MM_TABLES;
|
||||
|
||||
export async function storeConfigAndLicense(serverUrl: string, config: ClientConfig, license: ClientLicense) {
|
||||
try {
|
||||
// If we have credentials for this server then update the values in the database
|
||||
@@ -83,155 +74,6 @@ export async function storeConfig(serverUrl: string, config: ClientConfig | unde
|
||||
return [];
|
||||
}
|
||||
|
||||
export async function storeDataRetentionPolicies(serverUrl: string, data: DataRetentionPoliciesRequest, prepareRecordsOnly = false) {
|
||||
try {
|
||||
const {globalPolicy, teamPolicies, channelPolicies} = data;
|
||||
const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
const systems: IdValue[] = [{
|
||||
id: SYSTEM_IDENTIFIERS.DATA_RETENTION_POLICIES,
|
||||
value: globalPolicy || {},
|
||||
}, {
|
||||
id: SYSTEM_IDENTIFIERS.GRANULAR_DATA_RETENTION_POLICIES,
|
||||
value: {
|
||||
team: teamPolicies || [],
|
||||
channel: channelPolicies || [],
|
||||
},
|
||||
}];
|
||||
|
||||
return operator.handleSystem({
|
||||
systems,
|
||||
prepareRecordsOnly,
|
||||
});
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateLastDataRetentionRun(serverUrl: string, value?: number, prepareRecordsOnly = false) {
|
||||
try {
|
||||
const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
|
||||
const systems: IdValue[] = [{
|
||||
id: SYSTEM_IDENTIFIERS.LAST_DATA_RETENTION_RUN,
|
||||
value: value || Date.now(),
|
||||
}];
|
||||
|
||||
return operator.handleSystem({systems, prepareRecordsOnly});
|
||||
} catch (error) {
|
||||
logError('Failed updateLastDataRetentionRun', error);
|
||||
return {error};
|
||||
}
|
||||
}
|
||||
|
||||
export async function dataRetentionCleanup(serverUrl: string) {
|
||||
try {
|
||||
const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
|
||||
const isDataRetentionEnabled = await getIsDataRetentionEnabled(database);
|
||||
if (!isDataRetentionEnabled) {
|
||||
return {error: undefined};
|
||||
}
|
||||
|
||||
const lastRunAt = await getLastGlobalDataRetentionRun(database);
|
||||
const lastCleanedToday = new Date(lastRunAt).toDateString() === new Date().toDateString();
|
||||
|
||||
// Do not run if clean up is already done today
|
||||
if (lastRunAt && lastCleanedToday) {
|
||||
return {error: undefined};
|
||||
}
|
||||
|
||||
const globalPolicy = await getGlobalDataRetentionPolicy(database);
|
||||
const granularPoliciesData = await getGranularDataRetentionPolicies(database);
|
||||
|
||||
// Get global data retention cutoff
|
||||
let globalRetentionCutoff = 0;
|
||||
if (globalPolicy?.message_deletion_enabled) {
|
||||
globalRetentionCutoff = globalPolicy.message_retention_cutoff;
|
||||
}
|
||||
|
||||
// Get Granular data retention policies
|
||||
let teamPolicies: TeamDataRetentionPolicy[] = [];
|
||||
let channelPolicies: ChannelDataRetentionPolicy[] = [];
|
||||
if (granularPoliciesData) {
|
||||
teamPolicies = granularPoliciesData.team;
|
||||
channelPolicies = granularPoliciesData.channel;
|
||||
}
|
||||
|
||||
const channelsCutoffs: {[key: string]: number} = {};
|
||||
|
||||
// Get channel level cutoff from team policies
|
||||
for await (const teamPolicy of teamPolicies) {
|
||||
const {team_id, post_duration} = teamPolicy;
|
||||
const channelIds = await queryAllChannelsForTeam(database, team_id).fetchIds();
|
||||
if (channelIds.length) {
|
||||
const cutoff = getDataRetentionPolicyCutoff(post_duration);
|
||||
channelIds.forEach((channelId) => {
|
||||
channelsCutoffs[channelId] = cutoff;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Get channel level cutoff from channel policies
|
||||
channelPolicies.forEach(({channel_id, post_duration}) => {
|
||||
channelsCutoffs[channel_id] = getDataRetentionPolicyCutoff(post_duration);
|
||||
});
|
||||
|
||||
const conditions = [];
|
||||
|
||||
const channelIds = Object.keys(channelsCutoffs);
|
||||
if (channelIds.length) {
|
||||
// Fetch posts by channel level cutoff
|
||||
for (const channelId of channelIds) {
|
||||
const cutoff = channelsCutoffs[channelId];
|
||||
conditions.push(`(channel_id='${channelId}' AND create_at < ${cutoff})`);
|
||||
}
|
||||
|
||||
// Fetch posts by global cutoff which are not already fetched by channel level cutoff
|
||||
conditions.push(`(channel_id NOT IN ('${channelIds.join("','")}') AND create_at < ${globalRetentionCutoff})`);
|
||||
} else {
|
||||
conditions.push(`create_at < ${globalRetentionCutoff}`);
|
||||
}
|
||||
|
||||
const postIds = await database.get<PostModel>(POST).query(
|
||||
Q.unsafeSqlQuery(`SELECT * FROM ${POST} where ${conditions.join(' OR ')}`),
|
||||
).fetchIds();
|
||||
|
||||
if (postIds.length) {
|
||||
const batchSize = 1000;
|
||||
const deletePromises = [];
|
||||
for (let i = 0; i < postIds.length; i += batchSize) {
|
||||
const batch = postIds.slice(i, batchSize);
|
||||
deletePromises.push(
|
||||
deletePosts(serverUrl, batch),
|
||||
);
|
||||
}
|
||||
const deleteResult = await Promise.all(deletePromises);
|
||||
for (const {error} of deleteResult) {
|
||||
if (error) {
|
||||
return {error};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await updateLastDataRetentionRun(serverUrl);
|
||||
|
||||
return {error: undefined};
|
||||
} catch (error) {
|
||||
logError('An error occurred while performing data retention cleanup', error);
|
||||
return {error};
|
||||
}
|
||||
}
|
||||
|
||||
// Returns cutoff time based on the policy's post_duration
|
||||
function getDataRetentionPolicyCutoff(postDuration: number) {
|
||||
const periodDate = new Date();
|
||||
periodDate.setDate(periodDate.getDate() - postDuration);
|
||||
periodDate.setHours(0);
|
||||
periodDate.setMinutes(0);
|
||||
periodDate.setSeconds(0);
|
||||
return periodDate.getTime();
|
||||
}
|
||||
|
||||
export async function setLastServerVersionCheck(serverUrl: string, reset = false) {
|
||||
try {
|
||||
const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
|
||||
@@ -22,7 +22,7 @@ export async function removeUserFromTeam(serverUrl: string, teamId: string) {
|
||||
models.push(...system);
|
||||
}
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models, 'removeUserFromTeam');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ export async function addSearchToTeamSearchHistory(serverUrl: string, teamId: st
|
||||
}
|
||||
}
|
||||
|
||||
await operator.batchRecords(models, 'addSearchToTeamHistory');
|
||||
await operator.batchRecords(models);
|
||||
return {searchModel};
|
||||
} catch (error) {
|
||||
logError('Failed addSearchToTeamSearchHistory', error);
|
||||
|
||||
@@ -40,7 +40,7 @@ export const switchToGlobalThreads = async (serverUrl: string, teamId?: string,
|
||||
models.push(...history);
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords(models, 'switchToGlobalThreads');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
const isTabletDevice = await isTablet();
|
||||
@@ -84,7 +84,7 @@ export const switchToThread = async (serverUrl: string, rootId: string, isFromNo
|
||||
currentChannelId: channel.id,
|
||||
});
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models, 'switchToThread');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
} else {
|
||||
const modelPromises: Array<Promise<Model[]>> = [];
|
||||
@@ -97,7 +97,7 @@ export const switchToThread = async (serverUrl: string, rootId: string, isFromNo
|
||||
modelPromises.push(prepareCommonSystemValues(operator, commonValues));
|
||||
const models = (await Promise.all(modelPromises)).flat();
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models, 'switchToThread');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ export async function createThreadFromNewPost(serverUrl: string, post: Post, pre
|
||||
}
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords(models, 'createThreadFromNewPost');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
return {models};
|
||||
@@ -257,7 +257,7 @@ export async function processReceivedThreads(serverUrl: string, threads: Thread[
|
||||
}
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords(models, 'processReceivedThreads');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
return {models};
|
||||
} catch (error) {
|
||||
@@ -277,7 +277,7 @@ export async function markTeamThreadsAsRead(serverUrl: string, teamId: string, p
|
||||
record.viewedAt = Date.now();
|
||||
}));
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords(models, 'markTeamThreadsAsRead');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
return {models};
|
||||
} catch (error) {
|
||||
@@ -300,7 +300,7 @@ export async function markThreadAsViewed(serverUrl: string, threadId: string, pr
|
||||
});
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([thread], 'markThreadAsViewed');
|
||||
await operator.batchRecords([thread]);
|
||||
}
|
||||
|
||||
return {model: thread};
|
||||
@@ -327,7 +327,7 @@ export async function updateThread(serverUrl: string, threadId: string, updatedT
|
||||
record.unreadReplies = updatedThread.unread_replies ?? record.unreadReplies;
|
||||
});
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([model], 'updateThread');
|
||||
await operator.batchRecords([model]);
|
||||
}
|
||||
return {model};
|
||||
} catch (error) {
|
||||
@@ -341,7 +341,7 @@ export async function updateTeamThreadsSync(serverUrl: string, data: TeamThreads
|
||||
const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
const models = await operator.handleTeamThreadsSync({data: [data], prepareRecordsOnly});
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords(models, 'updateTeamThreadsSync');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
return {models};
|
||||
} catch (error) {
|
||||
|
||||
@@ -22,7 +22,7 @@ export async function setCurrentUserStatusOffline(serverUrl: string) {
|
||||
}
|
||||
|
||||
user.prepareStatus(General.OFFLINE);
|
||||
await operator.batchRecords([user], 'setCurrentUserStatusOffline');
|
||||
await operator.batchRecords([user]);
|
||||
return null;
|
||||
} catch (error) {
|
||||
logError('Failed setCurrentUserStatusOffline', error);
|
||||
@@ -54,7 +54,7 @@ export async function updateLocalCustomStatus(serverUrl: string, user: UserModel
|
||||
}
|
||||
}
|
||||
|
||||
await operator.batchRecords(models, 'updateLocalCustomStatus');
|
||||
await operator.batchRecords(models);
|
||||
|
||||
return {};
|
||||
} catch (error) {
|
||||
@@ -97,37 +97,27 @@ export const updateRecentCustomStatuses = async (serverUrl: string, customStatus
|
||||
export const updateLocalUser = async (
|
||||
serverUrl: string,
|
||||
userDetails: Partial<UserProfile> & { status?: string},
|
||||
userId?: string,
|
||||
) => {
|
||||
try {
|
||||
const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
|
||||
let user: UserModel | undefined;
|
||||
|
||||
if (userId) {
|
||||
user = await getUserById(database, userId);
|
||||
} else {
|
||||
user = await getCurrentUser(database);
|
||||
}
|
||||
|
||||
const user = await getCurrentUser(database);
|
||||
if (user) {
|
||||
const u = user;
|
||||
await database.write(async () => {
|
||||
await u.update((userRecord: UserModel) => {
|
||||
userRecord.authService = userDetails.auth_service ?? u.authService;
|
||||
userRecord.email = userDetails.email ?? u.email;
|
||||
userRecord.firstName = userDetails.first_name ?? u.firstName;
|
||||
userRecord.lastName = userDetails.last_name ?? u.lastName;
|
||||
userRecord.lastPictureUpdate = userDetails.last_picture_update ?? u.lastPictureUpdate;
|
||||
userRecord.locale = userDetails.locale ?? u.locale;
|
||||
userRecord.nickname = userDetails.nickname ?? u.nickname;
|
||||
userRecord.notifyProps = userDetails.notify_props ?? u.notifyProps;
|
||||
userRecord.position = userDetails?.position ?? u.position;
|
||||
userRecord.props = userDetails.props ?? u.props;
|
||||
userRecord.roles = userDetails.roles ?? u.roles;
|
||||
userRecord.status = userDetails?.status ?? u.status;
|
||||
userRecord.timezone = userDetails.timezone ?? u.timezone;
|
||||
userRecord.username = userDetails.username ?? u.username;
|
||||
await user.update((userRecord: UserModel) => {
|
||||
userRecord.authService = userDetails.auth_service ?? user.authService;
|
||||
userRecord.email = userDetails.email ?? user.email;
|
||||
userRecord.firstName = userDetails.first_name ?? user.firstName;
|
||||
userRecord.lastName = userDetails.last_name ?? user.lastName;
|
||||
userRecord.lastPictureUpdate = userDetails.last_picture_update ?? user.lastPictureUpdate;
|
||||
userRecord.locale = userDetails.locale ?? user.locale;
|
||||
userRecord.nickname = userDetails.nickname ?? user.nickname;
|
||||
userRecord.notifyProps = userDetails.notify_props ?? user.notifyProps;
|
||||
userRecord.position = userDetails?.position ?? user.position;
|
||||
userRecord.props = userDetails.props ?? user.props;
|
||||
userRecord.roles = userDetails.roles ?? user.roles;
|
||||
userRecord.status = userDetails?.status ?? user.status;
|
||||
userRecord.timezone = userDetails.timezone ?? user.timezone;
|
||||
userRecord.username = userDetails.username ?? user.username;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import {DeviceEventEmitter} from 'react-native';
|
||||
import {addChannelToDefaultCategory, storeCategories} from '@actions/local/category';
|
||||
import {removeCurrentUserFromChannel, setChannelDeleteAt, storeMyChannelsForTeam, switchToChannel} from '@actions/local/channel';
|
||||
import {switchToGlobalThreads} from '@actions/local/thread';
|
||||
import {updateLocalUser} from '@actions/local/user';
|
||||
import {loadCallForChannel} from '@calls/actions/calls';
|
||||
import {DeepLink, Events, General, Preferences, Screens} from '@constants';
|
||||
import DatabaseManager from '@database/manager';
|
||||
@@ -16,8 +15,8 @@ import {getTeammateNameDisplaySetting} from '@helpers/api/preference';
|
||||
import AppsManager from '@managers/apps_manager';
|
||||
import NetworkManager from '@managers/network_manager';
|
||||
import {getActiveServer} from '@queries/app/servers';
|
||||
import {prepareMyChannelsForTeam, getChannelById, getChannelByName, getMyChannel, getChannelInfo, queryMyChannelSettingsByIds, getMembersCountByChannelsId, deleteChannelMembership, queryChannelsById} from '@queries/servers/channel';
|
||||
import {queryDisplayNamePreferences} from '@queries/servers/preference';
|
||||
import {prepareMyChannelsForTeam, getChannelById, getChannelByName, getMyChannel, getChannelInfo, queryMyChannelSettingsByIds, getMembersCountByChannelsId, queryChannelsById} from '@queries/servers/channel';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {getCommonSystemValues, getConfig, getCurrentChannelId, getCurrentTeamId, getCurrentUserId, getLicense, setCurrentChannelId, setCurrentTeamAndChannelId} from '@queries/servers/system';
|
||||
import {getNthLastChannelFromTeam, getMyTeamById, getTeamByName, queryMyTeams, removeChannelFromTeamHistory} from '@queries/servers/team';
|
||||
import {getCurrentUser} from '@queries/servers/user';
|
||||
@@ -36,10 +35,9 @@ import {setDirectChannelVisible} from './preference';
|
||||
import {fetchRolesIfNeeded} from './role';
|
||||
import {forceLogoutIfNecessary} from './session';
|
||||
import {addCurrentUserToTeam, fetchTeamByName, removeCurrentUserFromTeam} from './team';
|
||||
import {fetchProfilesInChannel, fetchProfilesInGroupChannels, fetchProfilesPerChannels, fetchUsersByIds, updateUsersNoLongerVisible} from './user';
|
||||
import {fetchProfilesInGroupChannels, fetchProfilesPerChannels, fetchUsersByIds, updateUsersNoLongerVisible} from './user';
|
||||
|
||||
import type {Client} from '@client/rest';
|
||||
import type ClientError from '@client/rest/error';
|
||||
import type {Model} from '@nozbe/watermelondb';
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
import type {IntlShape} from 'react-intl';
|
||||
@@ -51,99 +49,6 @@ export type MyChannelsRequest = {
|
||||
error?: unknown;
|
||||
}
|
||||
|
||||
export type ChannelMembersRequest = {
|
||||
members?: ChannelMembership[];
|
||||
error?: unknown;
|
||||
}
|
||||
|
||||
export async function removeMemberFromChannel(serverUrl: string, channelId: string, userId: string) {
|
||||
try {
|
||||
const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
const client = NetworkManager.getClient(serverUrl);
|
||||
|
||||
await client.removeFromChannel(userId, channelId);
|
||||
await deleteChannelMembership(operator, userId, channelId);
|
||||
|
||||
return {error: undefined};
|
||||
} catch (error) {
|
||||
logError('removeMemberFromChannel', error);
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
|
||||
return {error};
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchChannelMembersByIds(serverUrl: string, channelId: string, userIds: string[], fetchOnly = false): Promise<ChannelMembersRequest> {
|
||||
try {
|
||||
const client = NetworkManager.getClient(serverUrl);
|
||||
const members = await client.getChannelMembersByIds(channelId, userIds);
|
||||
|
||||
if (!fetchOnly) {
|
||||
const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
if (operator && members.length) {
|
||||
const memberships = members.map((u) => ({
|
||||
channel_id: channelId,
|
||||
user_id: u.user_id,
|
||||
scheme_admin: u.scheme_admin,
|
||||
}));
|
||||
await operator.handleChannelMembership({
|
||||
channelMemberships: memberships,
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {members};
|
||||
} catch (error) {
|
||||
logError('fetchChannelMembersByIds', error);
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientError);
|
||||
return {error};
|
||||
}
|
||||
}
|
||||
export async function updateChannelMemberSchemeRoles(serverUrl: string, channelId: string, userId: string, isSchemeUser: boolean, isSchemeAdmin: boolean, fetchOnly = false) {
|
||||
try {
|
||||
const client = NetworkManager.getClient(serverUrl);
|
||||
await client.updateChannelMemberSchemeRoles(channelId, userId, isSchemeUser, isSchemeAdmin);
|
||||
|
||||
if (!fetchOnly) {
|
||||
return getMemberInChannel(serverUrl, channelId, userId);
|
||||
}
|
||||
|
||||
return {error: undefined};
|
||||
} catch (error) {
|
||||
logError('updateChannelMemberSchemeRoles', error);
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
|
||||
return {error};
|
||||
}
|
||||
}
|
||||
|
||||
export async function getMemberInChannel(serverUrl: string, channelId: string, userId: string, fetchOnly = false) {
|
||||
try {
|
||||
const client = NetworkManager.getClient(serverUrl);
|
||||
const member = await client.getMemberInChannel(channelId, userId);
|
||||
|
||||
if (!fetchOnly) {
|
||||
updateLocalUser(serverUrl, member, userId);
|
||||
}
|
||||
return {member, error: undefined};
|
||||
} catch (error) {
|
||||
logError('getMemberInChannel', error);
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
|
||||
return {error};
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchChannelMemberships(serverUrl: string, channelId: string, options: GetUsersOptions, fetchOnly = false) {
|
||||
const {users = []} = await fetchProfilesInChannel(serverUrl, channelId, undefined, options, fetchOnly);
|
||||
const userIds = users.map((u) => u.id);
|
||||
|
||||
// MM-49896 https://mattermost.atlassian.net/browse/MM-49896
|
||||
// We are not sure the getChannelMembers API returns the same members
|
||||
// from getProfilesInChannel. This guarantees a 1:1 match of the
|
||||
// user IDs
|
||||
const {members = []} = await fetchChannelMembersByIds(serverUrl, channelId, userIds, true);
|
||||
return {users, members};
|
||||
}
|
||||
|
||||
export async function addMembersToChannel(serverUrl: string, channelId: string, userIds: string[], postRootId = '', fetchOnly = false) {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
@@ -174,7 +79,7 @@ export async function addMembersToChannel(serverUrl: string, channelId: string,
|
||||
}));
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat(), 'addMembersToChannel');
|
||||
await operator.batchRecords(models.flat());
|
||||
}
|
||||
return {channelMemberships};
|
||||
} catch (error) {
|
||||
@@ -251,7 +156,7 @@ export async function createChannel(serverUrl: string, displayName: string, purp
|
||||
models.push(...categoriesModels.models);
|
||||
}
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models, 'createChannel');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
fetchChannelStats(serverUrl, channelData.id, false);
|
||||
EphemeralStore.creatingChannel = false;
|
||||
@@ -296,7 +201,7 @@ export async function patchChannel(serverUrl: string, channelPatch: Partial<Chan
|
||||
models.push(channel);
|
||||
}
|
||||
if (models?.length) {
|
||||
await operator.batchRecords(models.flat(), 'patchChannel');
|
||||
await operator.batchRecords(models.flat());
|
||||
}
|
||||
return {channel: channelData};
|
||||
} catch (error) {
|
||||
@@ -343,7 +248,7 @@ export async function leaveChannel(serverUrl: string, channelId: string) {
|
||||
models.push(...removeUserModels);
|
||||
}
|
||||
|
||||
await operator.batchRecords(models, 'leaveChannel');
|
||||
await operator.batchRecords(models);
|
||||
|
||||
if (isTabletDevice) {
|
||||
switchToLastChannel(serverUrl);
|
||||
@@ -393,7 +298,7 @@ export async function fetchChannelCreator(serverUrl: string, channelId: string,
|
||||
}));
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat(), 'fetchChannelCreator');
|
||||
await operator.batchRecords(models.flat());
|
||||
}
|
||||
|
||||
return {user};
|
||||
@@ -484,7 +389,7 @@ export async function fetchMyChannelsForTeam(serverUrl: string, teamId: string,
|
||||
const {models: catModels} = await storeCategories(serverUrl, categories, true, true); // Re-sync
|
||||
const models = (chModels || []).concat(catModels || []);
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models, 'fetchMyChannelsForTeam');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
setTeamLoading(serverUrl, false);
|
||||
}
|
||||
@@ -609,7 +514,7 @@ export async function fetchMissingDirectChannelsInfo(serverUrl: string, directCh
|
||||
}
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat(), 'fetchMissingDirectChannelInfo');
|
||||
await operator.batchRecords(models.flat());
|
||||
}
|
||||
|
||||
return {directChannels: updatedChannelsArray, users};
|
||||
@@ -624,7 +529,7 @@ export async function fetchDirectChannelsInfo(serverUrl: string, directChannels:
|
||||
return {error: `${serverUrl} database not found`};
|
||||
}
|
||||
|
||||
const preferences = await queryDisplayNamePreferences(database).fetch();
|
||||
const preferences = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS).fetch();
|
||||
const config = await getConfig(database);
|
||||
const license = await getLicense(database);
|
||||
const teammateDisplayNameSetting = getTeammateNameDisplaySetting(preferences, config?.LockTeammateNameDisplay, config?.TeammateNameDisplay, license);
|
||||
@@ -687,7 +592,7 @@ export async function joinChannel(serverUrl: string, teamId: string, channelId?:
|
||||
}
|
||||
if (flattenedModels?.length > 0) {
|
||||
try {
|
||||
await operator.batchRecords(flattenedModels, 'joinChannel');
|
||||
await operator.batchRecords(flattenedModels);
|
||||
} catch {
|
||||
logError('FAILED TO BATCH CHANNELS');
|
||||
}
|
||||
@@ -870,7 +775,7 @@ export async function createDirectChannel(serverUrl: string, userId: string, dis
|
||||
if (displayName) {
|
||||
created.display_name = displayName;
|
||||
} else {
|
||||
const preferences = await queryDisplayNamePreferences(database, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const preferences = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const license = await getLicense(database);
|
||||
const config = await getConfig(database);
|
||||
const teammateDisplayNameSetting = getTeammateNameDisplaySetting(preferences || [], config.LockTeammateNameDisplay, config.TeammateNameDisplay, license);
|
||||
@@ -913,7 +818,7 @@ export async function createDirectChannel(serverUrl: string, userId: string, dis
|
||||
models.push(...userModels);
|
||||
}
|
||||
|
||||
await operator.batchRecords(models, 'createDirectChannel');
|
||||
await operator.batchRecords(models);
|
||||
EphemeralStore.creatingDMorGMTeammates = [];
|
||||
fetchRolesIfNeeded(serverUrl, member.roles.split(' '));
|
||||
return {data: created};
|
||||
@@ -1012,7 +917,7 @@ export async function createGroupChannel(serverUrl: string, userIds: string[]) {
|
||||
return {data: created};
|
||||
}
|
||||
|
||||
const preferences = await queryDisplayNamePreferences(database, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const preferences = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const license = await getLicense(database);
|
||||
const config = await getConfig(database);
|
||||
const teammateDisplayNameSetting = getTeammateNameDisplaySetting(preferences || [], config.LockTeammateNameDisplay, config.TeammateNameDisplay, license);
|
||||
@@ -1047,7 +952,7 @@ export async function createGroupChannel(serverUrl: string, userIds: string[]) {
|
||||
}
|
||||
|
||||
models.push(...userModels);
|
||||
operator.batchRecords(models, 'createGroupChannel');
|
||||
operator.batchRecords(models);
|
||||
}
|
||||
}
|
||||
EphemeralStore.creatingDMorGMTeammates = [];
|
||||
@@ -1392,7 +1297,7 @@ export const convertChannelToPrivate = async (serverUrl: string, channelId: stri
|
||||
channel.prepareUpdate((c) => {
|
||||
c.type = General.PRIVATE_CHANNEL;
|
||||
});
|
||||
await operator.batchRecords([channel], 'convertChannelToPrivate');
|
||||
await operator.batchRecords([channel]);
|
||||
}
|
||||
return {error: undefined};
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {dataRetentionCleanup, setLastServerVersionCheck} from '@actions/local/systems';
|
||||
import {setLastServerVersionCheck} from '@actions/local/systems';
|
||||
import {fetchConfigAndLicense} from '@actions/remote/systems';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {prepareCommonSystemValues, getCurrentTeamId, getWebSocketLastDisconnected, getCurrentChannelId, getConfig, getLicense} from '@queries/servers/system';
|
||||
@@ -27,13 +27,10 @@ export async function appEntry(serverUrl: string, since = 0, isUpgrade = false)
|
||||
}
|
||||
}
|
||||
|
||||
// Run data retention cleanup
|
||||
await dataRetentionCleanup(serverUrl);
|
||||
|
||||
// clear lastUnreadChannelId
|
||||
const removeLastUnreadChannelId = await prepareCommonSystemValues(operator, {lastUnreadChannelId: ''});
|
||||
if (removeLastUnreadChannelId) {
|
||||
await operator.batchRecords(removeLastUnreadChannelId, 'appEntry - removeLastUnreadChannelId');
|
||||
await operator.batchRecords(removeLastUnreadChannelId);
|
||||
}
|
||||
|
||||
const {database} = operator;
|
||||
@@ -58,14 +55,14 @@ export async function appEntry(serverUrl: string, since = 0, isUpgrade = false)
|
||||
currentChannelId: isTabletDevice ? initialChannelId : undefined,
|
||||
});
|
||||
if (me?.length) {
|
||||
await operator.batchRecords(me, 'appEntry - upgrade store me');
|
||||
await operator.batchRecords(me);
|
||||
}
|
||||
}
|
||||
|
||||
await handleEntryAfterLoadNavigation(serverUrl, teamData.memberships || [], chData?.memberships || [], currentTeamId, currentChannelId, initialTeamId, initialChannelId);
|
||||
|
||||
const dt = Date.now();
|
||||
await operator.batchRecords(models, 'appEntry');
|
||||
await operator.batchRecords(models);
|
||||
logInfo('ENTRY MODELS BATCHING TOOK', `${Date.now() - dt}ms`);
|
||||
setTeamLoading(serverUrl, false);
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {dataRetentionCleanup} from '@actions/local/systems';
|
||||
import {fetchMissingDirectChannelsInfo, fetchMyChannelsForTeam, handleKickFromChannel, MyChannelsRequest} from '@actions/remote/channel';
|
||||
import {fetchGroupsForMember} from '@actions/remote/groups';
|
||||
import {fetchPostsForUnreadChannels} from '@actions/remote/post';
|
||||
@@ -178,7 +177,7 @@ export const fetchAppEntryData = async (serverUrl: string, sinceArg: number, ini
|
||||
if (!initialTeamId && teamData.teams?.length && teamData.memberships?.length) {
|
||||
// If no initial team was set in the database but got teams in the response
|
||||
const config = await getConfig(database);
|
||||
const teamOrderPreference = getPreferenceValue<string>(prefData.preferences || [], Preferences.CATEGORIES.TEAMS_ORDER, '', '');
|
||||
const teamOrderPreference = getPreferenceValue(prefData.preferences || [], Preferences.TEAMS_ORDER, '', '') as string;
|
||||
const teamMembers = new Set(teamData.memberships.filter((m) => m.delete_at === 0).map((m) => m.team_id));
|
||||
const myTeams = teamData.teams!.filter((t) => teamMembers.has(t.id));
|
||||
const defaultTeam = selectDefaultTeam(myTeams, meData.user?.locale || DEFAULT_LOCALE, teamOrderPreference, config?.ExperimentalPrimaryTeam);
|
||||
@@ -378,9 +377,7 @@ export const syncOtherServers = async (serverUrl: string) => {
|
||||
for (const server of servers) {
|
||||
if (server.url !== serverUrl && server.lastActiveAt > 0) {
|
||||
registerDeviceToken(server.url);
|
||||
syncAllChannelMembersAndThreads(server.url).then(() => {
|
||||
dataRetentionCleanup(server.url);
|
||||
});
|
||||
syncAllChannelMembersAndThreads(server.url);
|
||||
autoUpdateTimezone(server.url);
|
||||
}
|
||||
}
|
||||
@@ -431,7 +428,7 @@ const graphQLSyncAllChannelMembers = async (serverUrl: string) => {
|
||||
const modelPromises = await prepareMyChannelsForTeam(operator, '', channels, memberships, undefined, true);
|
||||
const models = (await Promise.all(modelPromises)).flat();
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models, 'graphQLSyncAllChannelMembers');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
import {storeConfigAndLicense} from '@actions/local/systems';
|
||||
import {fetchGroupsForMember} from '@actions/remote/groups';
|
||||
import {fetchPostsForUnreadChannels} from '@actions/remote/post';
|
||||
import {fetchDataRetentionPolicy} from '@actions/remote/systems';
|
||||
import {MyTeamsRequest, updateCanJoinTeams} from '@actions/remote/team';
|
||||
import {syncTeamThreads} from '@actions/remote/thread';
|
||||
import {autoUpdateTimezone, updateAllUsersSince} from '@actions/remote/user';
|
||||
@@ -16,7 +15,7 @@ import {selectDefaultTeam} from '@helpers/api/team';
|
||||
import {queryAllChannels, queryAllChannelsForTeam} from '@queries/servers/channel';
|
||||
import {prepareModels, truncateCrtRelatedTables} from '@queries/servers/entry';
|
||||
import {getHasCRTChanged} from '@queries/servers/preference';
|
||||
import {getConfig, getIsDataRetentionEnabled} from '@queries/servers/system';
|
||||
import {getConfig} from '@queries/servers/system';
|
||||
import {filterAndTransformRoles, getMemberChannelsFromGQLQuery, getMemberTeamsFromGQLQuery, gqlToClientChannelMembership, gqlToClientPreference, gqlToClientSidebarCategory, gqlToClientTeamMembership, gqlToClientUser} from '@utils/graphql';
|
||||
import {logDebug} from '@utils/log';
|
||||
import {processIsCRTEnabled} from '@utils/thread';
|
||||
@@ -82,7 +81,7 @@ export async function deferredAppEntryGraphQLActions(
|
||||
modelPromises.push(operator.handleRole({roles, prepareRecordsOnly: true}));
|
||||
}
|
||||
const models = (await Promise.all(modelPromises)).flat();
|
||||
operator.batchRecords(models, 'deferredAppEntryActions');
|
||||
operator.batchRecords(models);
|
||||
|
||||
setTimeout(() => {
|
||||
if (result.chData?.channels?.length && result.chData.memberships?.length) {
|
||||
@@ -217,7 +216,7 @@ export const entryGQL = async (serverUrl: string, currentTeamId?: string, curren
|
||||
if (!teamData.teams.length) {
|
||||
initialTeamId = '';
|
||||
} else if (!initialTeamId || !teamData.teams.find((t) => t.id === currentTeamId && t.delete_at === 0)) {
|
||||
const teamOrderPreference = getPreferenceValue<string>(prefData.preferences || [], Preferences.CATEGORIES.TEAMS_ORDER, '', '');
|
||||
const teamOrderPreference = getPreferenceValue(prefData.preferences || [], Preferences.TEAMS_ORDER, '', '') as string;
|
||||
initialTeamId = selectDefaultTeam(teamData.teams, meData.user.locale, teamOrderPreference, config.ExperimentalPrimaryTeam)?.id || '';
|
||||
}
|
||||
const gqlRoles = [
|
||||
@@ -265,12 +264,6 @@ export const entry = async (serverUrl: string, teamId?: string, channelId?: stri
|
||||
result = entryRest(serverUrl, teamId, channelId, since);
|
||||
}
|
||||
|
||||
// Fetch data retention policies
|
||||
const isDataRetentionEnabled = await getIsDataRetentionEnabled(database);
|
||||
if (isDataRetentionEnabled) {
|
||||
fetchDataRetentionPolicy(serverUrl);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ export async function loginEntry({serverUrl, user, deviceToken}: AfterLoginArgs)
|
||||
setCurrentTeamAndChannelId(operator, initialTeamId, '');
|
||||
}
|
||||
|
||||
await operator.batchRecords(models, 'loginEntry');
|
||||
await operator.batchRecords(models);
|
||||
setTeamLoading(serverUrl, false);
|
||||
|
||||
const config = clData.config || {} as ClientConfig;
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
|
||||
import {switchToChannelById} from '@actions/remote/channel';
|
||||
import {fetchAndSwitchToThread} from '@actions/remote/thread';
|
||||
import {Screens} from '@constants';
|
||||
import {Preferences, Screens} from '@constants';
|
||||
import {getDefaultThemeByAppearance} from '@context/theme';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {getMyChannel} from '@queries/servers/channel';
|
||||
import {queryThemePreferences} from '@queries/servers/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {getConfig, getCurrentTeamId, getLicense, getWebSocketLastDisconnected, setCurrentTeamAndChannelId} from '@queries/servers/system';
|
||||
import {getMyTeamById} from '@queries/servers/team';
|
||||
import {getIsCRTEnabled} from '@queries/servers/thread';
|
||||
@@ -53,7 +53,7 @@ export async function pushNotificationEntry(serverUrl: string, notification: Not
|
||||
// When opening the app from a push notification the theme may not be set in the EphemeralStore
|
||||
// causing the goToScreen to use the Appearance theme instead and that causes the screen background color to potentially
|
||||
// not match the theme
|
||||
const themes = await queryThemePreferences(database, teamId).fetch();
|
||||
const themes = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_THEME, teamId).fetch();
|
||||
let theme = getDefaultThemeByAppearance();
|
||||
if (themes.length) {
|
||||
theme = setThemeDefaults(JSON.parse(themes[0].value) as Theme);
|
||||
@@ -136,7 +136,7 @@ export async function pushNotificationEntry(serverUrl: string, notification: Not
|
||||
await NavigationStore.waitUntilScreenHasLoaded(Screens.THREAD);
|
||||
}
|
||||
|
||||
await operator.batchRecords(models, 'pushNotificationEntry');
|
||||
await operator.batchRecords(models);
|
||||
setTeamLoading(serverUrl, false);
|
||||
|
||||
const {id: currentUserId, locale: currentUserLocale} = (await getCurrentUser(operator.database))!;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
import {DOWNLOAD_TIMEOUT} from '@constants/network';
|
||||
import NetworkManager from '@managers/network_manager';
|
||||
import {logDebug} from '@utils/log';
|
||||
|
||||
import {forceLogoutIfNecessary} from './session';
|
||||
|
||||
@@ -33,11 +32,10 @@ export const uploadFile = (
|
||||
let client: Client;
|
||||
try {
|
||||
client = NetworkManager.getClient(serverUrl);
|
||||
return {cancel: client.uploadPostAttachment(file, channelId, onProgress, onComplete, onError, skipBytes)};
|
||||
} catch (error) {
|
||||
logDebug('uploadFile', error);
|
||||
return {error: error as ClientError};
|
||||
}
|
||||
return {cancel: client.uploadPostAttachment(file, channelId, onProgress, onComplete, onError, skipBytes)};
|
||||
};
|
||||
|
||||
export const fetchPublicLink = async (serverUrl: string, fileId: string) => {
|
||||
|
||||
@@ -83,7 +83,7 @@ export const fetchGroupsForChannel = async (serverUrl: string, channelId: string
|
||||
]);
|
||||
|
||||
if (!fetchOnly) {
|
||||
await operator.batchRecords([...groups, ...groupChannels], 'fetchGroupsForChannel');
|
||||
await operator.batchRecords([...groups, ...groupChannels]);
|
||||
}
|
||||
|
||||
return {groups, groupChannels};
|
||||
@@ -110,7 +110,7 @@ export const fetchGroupsForTeam = async (serverUrl: string, teamId: string, fetc
|
||||
]);
|
||||
|
||||
if (!fetchOnly) {
|
||||
await operator.batchRecords([...groups, ...groupTeams], 'fetchGroupsForTeam');
|
||||
await operator.batchRecords([...groups, ...groupTeams]);
|
||||
}
|
||||
|
||||
return {groups, groupTeams};
|
||||
@@ -136,7 +136,7 @@ export const fetchGroupsForMember = async (serverUrl: string, userId: string, fe
|
||||
]);
|
||||
|
||||
if (!fetchOnly) {
|
||||
await operator.batchRecords([...groups, ...groupMemberships], 'fetchGroupsForMember');
|
||||
await operator.batchRecords([...groups, ...groupMemberships]);
|
||||
}
|
||||
|
||||
return {groups, groupMemberships};
|
||||
|
||||
@@ -128,7 +128,7 @@ export async function createPost(serverUrl: string, post: Partial<Post>, files:
|
||||
initialPostModels.push(...reactionModels);
|
||||
}
|
||||
|
||||
await operator.batchRecords(initialPostModels, 'createPost - initial');
|
||||
await operator.batchRecords(initialPostModels);
|
||||
|
||||
const isCRTEnabled = await getIsCRTEnabled(database);
|
||||
|
||||
@@ -167,7 +167,7 @@ export async function createPost(serverUrl: string, post: Partial<Post>, files:
|
||||
models.push(...threadModels);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models, 'createPost - failure');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
return {data: true};
|
||||
@@ -192,7 +192,7 @@ export async function createPost(serverUrl: string, post: Partial<Post>, files:
|
||||
models.push(...threadModels);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models, 'createPost - success');
|
||||
await operator.batchRecords(models);
|
||||
|
||||
newPost = created;
|
||||
|
||||
@@ -237,7 +237,7 @@ export const retryFailedPost = async (serverUrl: string, post: PostModel) => {
|
||||
p.props = newPost.props;
|
||||
p.updateAt = timestamp;
|
||||
});
|
||||
await operator.batchRecords([post], 'retryFailedPost - first update');
|
||||
await operator.batchRecords([post]);
|
||||
|
||||
const created = await client.createPost(newPost);
|
||||
const models = await operator.handlePosts({
|
||||
@@ -253,7 +253,7 @@ export const retryFailedPost = async (serverUrl: string, post: PostModel) => {
|
||||
models.push(member);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models, 'retryFailedPost - success update');
|
||||
await operator.batchRecords(models);
|
||||
} catch (error) {
|
||||
if (isServerError(error) && (
|
||||
error.server_error_id === ServerErrors.DELETED_ROOT_POST_ERROR ||
|
||||
@@ -268,7 +268,7 @@ export const retryFailedPost = async (serverUrl: string, post: PostModel) => {
|
||||
failed: true,
|
||||
};
|
||||
});
|
||||
await operator.batchRecords([post], 'retryFailedPost - error update');
|
||||
await operator.batchRecords([post]);
|
||||
}
|
||||
|
||||
return {error};
|
||||
@@ -375,7 +375,7 @@ export async function fetchPosts(serverUrl: string, channelId: string, page = 0,
|
||||
models.push(...threadModels);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models, 'fetchPosts');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
@@ -434,7 +434,7 @@ export async function fetchPostsBefore(serverUrl: string, channelId: string, pos
|
||||
}
|
||||
}
|
||||
|
||||
await operator.batchRecords(models, 'fetchPostsBefore');
|
||||
await operator.batchRecords(models);
|
||||
} catch (error) {
|
||||
logError('FETCH POSTS BEFORE ERROR', error);
|
||||
}
|
||||
@@ -444,7 +444,7 @@ export async function fetchPostsBefore(serverUrl: string, channelId: string, pos
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
|
||||
if (activeServerUrl === serverUrl) {
|
||||
DeviceEventEmitter.emit(Events.LOADING_CHANNEL_POSTS, false);
|
||||
DeviceEventEmitter.emit(Events.LOADING_CHANNEL_POSTS, true);
|
||||
}
|
||||
return {error};
|
||||
}
|
||||
@@ -489,7 +489,7 @@ export async function fetchPostsSince(serverUrl: string, channelId: string, sinc
|
||||
models.push(...threadModels);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models, 'fetchPostsSince');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
@@ -621,7 +621,7 @@ export async function fetchPostThread(serverUrl: string, postId: string, options
|
||||
models.push(...threadModels);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models, 'fetchPostThread');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
setFetchingThreadState(postId, false);
|
||||
return {posts: extractRecordsForTable<PostModel>(posts, MM_TABLES.SERVER.POST)};
|
||||
@@ -697,7 +697,7 @@ export async function fetchPostsAround(serverUrl: string, channelId: string, pos
|
||||
models.push(...threadModels);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models, 'fetchPostsAround');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
return {posts: extractRecordsForTable<PostModel>(posts, MM_TABLES.SERVER.POST)};
|
||||
@@ -751,7 +751,7 @@ export async function fetchMissingChannelsFromPosts(serverUrl: string, posts: Po
|
||||
return mdls;
|
||||
});
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models, 'fetchMissingChannelsFromPosts');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -809,7 +809,7 @@ export async function fetchPostById(serverUrl: string, postId: string, fetchOnly
|
||||
}
|
||||
}
|
||||
|
||||
await operator.batchRecords(models, 'fetchPostById');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
return {post};
|
||||
@@ -1036,7 +1036,7 @@ export async function fetchSavedPosts(serverUrl: string, teamId?: string, channe
|
||||
return mdls;
|
||||
});
|
||||
|
||||
await operator.batchRecords(models, 'fetchSavedPosts');
|
||||
await operator.batchRecords(models);
|
||||
|
||||
return {
|
||||
order,
|
||||
@@ -1118,7 +1118,7 @@ export async function fetchPinnedPosts(serverUrl: string, channelId: string) {
|
||||
return mdls;
|
||||
});
|
||||
|
||||
await operator.batchRecords(models, 'fetchPinnedPosts');
|
||||
await operator.batchRecords(models);
|
||||
|
||||
return {
|
||||
order,
|
||||
|
||||
@@ -5,12 +5,14 @@ import {General, Preferences} from '@constants';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import NetworkManager from '@managers/network_manager';
|
||||
import {getChannelById} from '@queries/servers/channel';
|
||||
import {querySavedPostsPreferences} from '@queries/servers/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {getCurrentUserId} from '@queries/servers/system';
|
||||
import {getUserIdFromChannelName} from '@utils/user';
|
||||
|
||||
import {forceLogoutIfNecessary} from './session';
|
||||
|
||||
const {CATEGORY_DIRECT_CHANNEL_SHOW, CATEGORY_GROUP_CHANNEL_SHOW, CATEGORY_FAVORITE_CHANNEL, CATEGORY_SAVED_POST} = Preferences;
|
||||
|
||||
export type MyPreferencesRequest = {
|
||||
preferences?: PreferenceType[];
|
||||
error?: unknown;
|
||||
@@ -54,7 +56,7 @@ export const saveFavoriteChannel = async (serverUrl: string, channelId: string,
|
||||
try {
|
||||
const userId = await getCurrentUserId(operator.database);
|
||||
const favPref: PreferenceType = {
|
||||
category: Preferences.CATEGORIES.FAVORITE_CHANNEL,
|
||||
category: CATEGORY_FAVORITE_CHANNEL,
|
||||
name: channelId,
|
||||
user_id: userId,
|
||||
value: String(isFavorite),
|
||||
@@ -75,7 +77,7 @@ export const savePostPreference = async (serverUrl: string, postId: string) => {
|
||||
const userId = await getCurrentUserId(operator.database);
|
||||
const pref: PreferenceType = {
|
||||
user_id: userId,
|
||||
category: Preferences.CATEGORIES.SAVED_POST,
|
||||
category: CATEGORY_SAVED_POST,
|
||||
name: postId,
|
||||
value: 'true',
|
||||
};
|
||||
@@ -114,15 +116,23 @@ export const savePreference = async (serverUrl: string, preferences: PreferenceT
|
||||
};
|
||||
|
||||
export const deleteSavedPost = async (serverUrl: string, postId: string) => {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
return {error: `${serverUrl} database not found`};
|
||||
}
|
||||
let client;
|
||||
try {
|
||||
const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
const client = NetworkManager.getClient(serverUrl);
|
||||
const userId = await getCurrentUserId(database);
|
||||
const records = await querySavedPostsPreferences(database, postId).fetch();
|
||||
client = NetworkManager.getClient(serverUrl);
|
||||
} catch (error) {
|
||||
return {error};
|
||||
}
|
||||
try {
|
||||
const userId = await getCurrentUserId(operator.database);
|
||||
const records = await queryPreferencesByCategoryAndName(operator.database, CATEGORY_SAVED_POST, postId).fetch();
|
||||
const postPreferenceRecord = records.find((r) => postId === r.name);
|
||||
const pref = {
|
||||
user_id: userId,
|
||||
category: Preferences.CATEGORIES.SAVED_POST,
|
||||
category: CATEGORY_SAVED_POST,
|
||||
name: postId,
|
||||
value: 'true',
|
||||
};
|
||||
@@ -151,8 +161,7 @@ export const setDirectChannelVisible = async (serverUrl: string, channelId: stri
|
||||
const channel = await getChannelById(database, channelId);
|
||||
if (channel?.type === General.DM_CHANNEL || channel?.type === General.GM_CHANNEL) {
|
||||
const userId = await getCurrentUserId(database);
|
||||
const {DIRECT_CHANNEL_SHOW, GROUP_CHANNEL_SHOW} = Preferences.CATEGORIES;
|
||||
const category = channel.type === General.DM_CHANNEL ? DIRECT_CHANNEL_SHOW : GROUP_CHANNEL_SHOW;
|
||||
const category = channel.type === General.DM_CHANNEL ? CATEGORY_DIRECT_CHANNEL_SHOW : CATEGORY_GROUP_CHANNEL_SHOW;
|
||||
const name = channel.type === General.DM_CHANNEL ? getUserIdFromChannelName(userId, channel.name) : channelId;
|
||||
const pref: PreferenceType = {
|
||||
user_id: userId,
|
||||
@@ -176,7 +185,7 @@ export const savePreferredSkinTone = async (serverUrl: string, skinCode: string)
|
||||
const userId = await getCurrentUserId(database);
|
||||
const pref: PreferenceType = {
|
||||
user_id: userId,
|
||||
category: Preferences.CATEGORIES.EMOJI,
|
||||
category: Preferences.CATEGORY_EMOJI,
|
||||
name: Preferences.EMOJI_SKINTONE,
|
||||
value: skinCode,
|
||||
};
|
||||
|
||||
@@ -51,7 +51,7 @@ export async function addReaction(serverUrl: string, postId: string, emojiName:
|
||||
models.push(...recent);
|
||||
}
|
||||
|
||||
await operator.batchRecords(models, 'addReaction');
|
||||
await operator.batchRecords(models);
|
||||
|
||||
return {reaction};
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import {selectDefaultTeam} from '@helpers/api/team';
|
||||
import NetworkManager from '@managers/network_manager';
|
||||
import {prepareCategoriesAndCategoriesChannels} from '@queries/servers/categories';
|
||||
import {prepareMyChannelsForTeam} from '@queries/servers/channel';
|
||||
import {prepareMyPreferences, queryDisplayNamePreferences} from '@queries/servers/preference';
|
||||
import {prepareMyPreferences, queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {prepareCommonSystemValues, getConfig, getLicense} from '@queries/servers/system';
|
||||
import {prepareMyTeams} from '@queries/servers/team';
|
||||
import {getCurrentUser} from '@queries/servers/user';
|
||||
@@ -60,7 +60,7 @@ export async function retryInitialTeamAndChannel(serverUrl: string) {
|
||||
|
||||
// select initial team
|
||||
if (!clData.error && !prefData.error && !teamData.error) {
|
||||
const teamOrderPreference = getPreferenceValue<string>(prefData.preferences!, Preferences.CATEGORIES.TEAMS_ORDER, '', '');
|
||||
const teamOrderPreference = getPreferenceValue(prefData.preferences!, Preferences.TEAMS_ORDER, '', '') as string;
|
||||
const teamRoles: string[] = [];
|
||||
const teamMembers = new Set<string>();
|
||||
|
||||
@@ -117,7 +117,7 @@ export async function retryInitialTeamAndChannel(serverUrl: string) {
|
||||
),
|
||||
])).flat();
|
||||
|
||||
await operator.batchRecords(models, 'retryInitialTeamAndChannel');
|
||||
await operator.batchRecords(models);
|
||||
|
||||
const directChannels = chData!.channels!.filter(isDMorGM);
|
||||
const channelsToFetchProfiles = new Set<Channel>(directChannels);
|
||||
@@ -157,7 +157,7 @@ export async function retryInitialChannel(serverUrl: string, teamId: string) {
|
||||
return {error: true};
|
||||
}
|
||||
|
||||
const prefs = await queryDisplayNamePreferences(database, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const prefs = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const preferences: PreferenceType[] = prefs.map((p) => ({
|
||||
category: p.category,
|
||||
name: p.name,
|
||||
@@ -196,7 +196,7 @@ export async function retryInitialChannel(serverUrl: string, teamId: string) {
|
||||
prepareCommonSystemValues(operator, {currentChannelId: initialChannel?.id}),
|
||||
])).flat();
|
||||
|
||||
await operator.batchRecords(models, 'retryInitialChannel');
|
||||
await operator.batchRecords(models);
|
||||
|
||||
const directChannels = chData!.channels!.filter(isDMorGM);
|
||||
const channelsToFetchProfiles = new Set<Channel>(directChannels);
|
||||
|
||||
@@ -103,7 +103,7 @@ export const searchPosts = async (serverUrl: string, teamId: string, params: Pos
|
||||
return mdls;
|
||||
});
|
||||
|
||||
await operator.batchRecords(models, 'searchPosts');
|
||||
await operator.batchRecords(models);
|
||||
return {
|
||||
order,
|
||||
posts: postsArray,
|
||||
|
||||
@@ -21,6 +21,7 @@ import {scheduleExpiredNotification} from '@utils/notification';
|
||||
import {getCSRFFromCookie} from '@utils/security';
|
||||
|
||||
import {loginEntry} from './entry';
|
||||
import {fetchDataRetentionPolicy} from './systems';
|
||||
|
||||
import type ClientError from '@client/rest/error';
|
||||
import type {LoginArgs} from '@typings/database/database';
|
||||
@@ -41,6 +42,11 @@ export const completeLogin = async (serverUrl: string) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Data retention
|
||||
if (config?.DataRetentionEnableMessageDeletion === 'true' && license?.IsLicensed === 'true' && license?.DataRetention === 'true') {
|
||||
fetchDataRetentionPolicy(serverUrl);
|
||||
}
|
||||
|
||||
await DatabaseManager.setActiveServerDatabase(serverUrl);
|
||||
|
||||
const systems: IdValue[] = [];
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {storeConfigAndLicense, storeDataRetentionPolicies} from '@actions/local/systems';
|
||||
import {storeConfigAndLicense} from '@actions/local/systems';
|
||||
import {forceLogoutIfNecessary} from '@actions/remote/session';
|
||||
import {SYSTEM_IDENTIFIERS} from '@constants/database';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import NetworkManager from '@managers/network_manager';
|
||||
import {getCurrentUserId} from '@queries/servers/system';
|
||||
import {logError} from '@utils/log';
|
||||
|
||||
import type ClientError from '@client/rest/error';
|
||||
|
||||
@@ -15,47 +16,7 @@ export type ConfigAndLicenseRequest = {
|
||||
error?: unknown;
|
||||
}
|
||||
|
||||
export type DataRetentionPoliciesRequest = {
|
||||
globalPolicy?: GlobalDataRetentionPolicy;
|
||||
teamPolicies?: TeamDataRetentionPolicy[];
|
||||
channelPolicies?: ChannelDataRetentionPolicy[];
|
||||
error?: unknown;
|
||||
}
|
||||
|
||||
export const fetchDataRetentionPolicy = async (serverUrl: string, fetchOnly = false): Promise<DataRetentionPoliciesRequest> => {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
return {error: `${serverUrl} database not found`};
|
||||
}
|
||||
|
||||
try {
|
||||
const {data: globalPolicy, error: globalPolicyError} = await fetchGlobalDataRetentionPolicy(serverUrl);
|
||||
const {data: teamPolicies, error: teamPoliciesError} = await fetchAllGranularDataRetentionPolicies(serverUrl);
|
||||
const {data: channelPolicies, error: channelPoliciesError} = await fetchAllGranularDataRetentionPolicies(serverUrl, true);
|
||||
|
||||
const hasError = globalPolicyError || teamPoliciesError || channelPoliciesError;
|
||||
if (hasError) {
|
||||
return hasError;
|
||||
}
|
||||
|
||||
const data = {
|
||||
globalPolicy,
|
||||
teamPolicies: teamPolicies as TeamDataRetentionPolicy[],
|
||||
channelPolicies: channelPolicies as ChannelDataRetentionPolicy[],
|
||||
};
|
||||
|
||||
if (!fetchOnly) {
|
||||
await storeDataRetentionPolicies(serverUrl, data);
|
||||
}
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientError);
|
||||
return {error};
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchGlobalDataRetentionPolicy = async (serverUrl: string): Promise<{data?: GlobalDataRetentionPolicy; error?: unknown}> => {
|
||||
export const fetchDataRetentionPolicy = async (serverUrl: string) => {
|
||||
let client;
|
||||
try {
|
||||
client = NetworkManager.getClient(serverUrl);
|
||||
@@ -63,47 +24,28 @@ export const fetchGlobalDataRetentionPolicy = async (serverUrl: string): Promise
|
||||
return {error};
|
||||
}
|
||||
|
||||
let data = {};
|
||||
try {
|
||||
const data = await client.getGlobalDataRetentionPolicy();
|
||||
return {data};
|
||||
data = await client.getDataRetentionPolicy();
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientError);
|
||||
return {error};
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchAllGranularDataRetentionPolicies = async (
|
||||
serverUrl: string,
|
||||
isChannel = false,
|
||||
page = 0,
|
||||
policies: Array<TeamDataRetentionPolicy | ChannelDataRetentionPolicy> = [],
|
||||
): Promise<{data?: Array<TeamDataRetentionPolicy | ChannelDataRetentionPolicy>; error?: unknown}> => {
|
||||
let client;
|
||||
try {
|
||||
client = NetworkManager.getClient(serverUrl);
|
||||
} catch (error) {
|
||||
return {error};
|
||||
}
|
||||
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
return {error: `${serverUrl} database not found`};
|
||||
if (operator) {
|
||||
const systems: IdValue[] = [{
|
||||
id: SYSTEM_IDENTIFIERS.DATA_RETENTION_POLICIES,
|
||||
value: JSON.stringify(data),
|
||||
}];
|
||||
|
||||
operator.handleSystem({systems, prepareRecordsOnly: false}).
|
||||
catch((error) => {
|
||||
logError('An error occurred while saving data retention policies', error);
|
||||
});
|
||||
}
|
||||
|
||||
const {database} = operator;
|
||||
|
||||
const currentUserId = await getCurrentUserId(database);
|
||||
let data;
|
||||
if (isChannel) {
|
||||
data = await client.getChannelDataRetentionPolicies(currentUserId, page);
|
||||
} else {
|
||||
data = await client.getTeamDataRetentionPolicies(currentUserId, page);
|
||||
}
|
||||
policies.push(...data.policies);
|
||||
if (policies.length < data.total_count) {
|
||||
await fetchAllGranularDataRetentionPolicies(serverUrl, isChannel, page + 1, policies);
|
||||
}
|
||||
return {data: policies};
|
||||
return data;
|
||||
};
|
||||
|
||||
export const fetchConfigAndLicense = async (serverUrl: string, fetchOnly = false): Promise<ConfigAndLicenseRequest> => {
|
||||
|
||||
@@ -86,7 +86,7 @@ export async function addUserToTeam(serverUrl: string, teamId: string, userId: s
|
||||
prepareCategoriesAndCategoriesChannels(operator, categories || [], true),
|
||||
])).flat();
|
||||
|
||||
await operator.batchRecords(models, 'addUserToTeam');
|
||||
await operator.batchRecords(models);
|
||||
setTeamLoading(serverUrl, false);
|
||||
loadEventSent = false;
|
||||
|
||||
@@ -114,54 +114,6 @@ export async function addUserToTeam(serverUrl: string, teamId: string, userId: s
|
||||
}
|
||||
}
|
||||
|
||||
export async function addUsersToTeam(serverUrl: string, teamId: string, userIds: string[], fetchOnly = false) {
|
||||
try {
|
||||
const client = NetworkManager.getClient(serverUrl);
|
||||
const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
EphemeralStore.startAddingToTeam(teamId);
|
||||
|
||||
const members = await client.addUsersToTeamGracefully(teamId, userIds);
|
||||
|
||||
if (!fetchOnly) {
|
||||
const teamMemberships: TeamMembership[] = [];
|
||||
const roles = [];
|
||||
|
||||
for (const {member} of members) {
|
||||
teamMemberships.push(member);
|
||||
roles.push(...member.roles.split(' '));
|
||||
}
|
||||
|
||||
fetchRolesIfNeeded(serverUrl, Array.from(new Set(roles)));
|
||||
|
||||
if (operator) {
|
||||
await operator.handleTeamMemberships({teamMemberships, prepareRecordsOnly: true});
|
||||
}
|
||||
}
|
||||
|
||||
EphemeralStore.finishAddingToTeam(teamId);
|
||||
return {members};
|
||||
} catch (error) {
|
||||
if (EphemeralStore.isAddingToTeam(teamId)) {
|
||||
EphemeralStore.finishAddingToTeam(teamId);
|
||||
}
|
||||
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientError);
|
||||
return {error};
|
||||
}
|
||||
}
|
||||
|
||||
export async function sendEmailInvitesToTeam(serverUrl: string, teamId: string, emails: string[]) {
|
||||
try {
|
||||
const client = NetworkManager.getClient(serverUrl);
|
||||
const members = await client.sendEmailInvitesToTeamGracefully(teamId, emails);
|
||||
|
||||
return {members};
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientError);
|
||||
return {error};
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchMyTeams(serverUrl: string, fetchOnly = false): Promise<MyTeamsRequest> {
|
||||
let client;
|
||||
try {
|
||||
@@ -199,7 +151,7 @@ export async function fetchMyTeams(serverUrl: string, fetchOnly = false): Promis
|
||||
const models = await Promise.all(modelPromises);
|
||||
const flattenedModels = models.flat();
|
||||
if (flattenedModels.length > 0) {
|
||||
await operator.batchRecords(flattenedModels, 'fetchMyTeams');
|
||||
await operator.batchRecords(flattenedModels);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -233,7 +185,7 @@ export async function fetchMyTeam(serverUrl: string, teamId: string, fetchOnly =
|
||||
const models = await Promise.all(modelPromises);
|
||||
const flattenedModels = models.flat();
|
||||
if (flattenedModels?.length > 0) {
|
||||
await operator.batchRecords(flattenedModels, 'fetchMyTeam');
|
||||
await operator.batchRecords(flattenedModels);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -362,7 +314,7 @@ export async function fetchTeamByName(serverUrl: string, teamName: string, fetch
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (operator) {
|
||||
const models = await operator.handleTeam({teams: [team], prepareRecordsOnly: true});
|
||||
await operator.batchRecords(models, 'fetchTeamByName');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,7 +393,7 @@ export async function handleTeamChange(serverUrl: string, teamId: string) {
|
||||
}
|
||||
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models, 'handleTeamChange');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
DeviceEventEmitter.emit(Events.TEAM_SWITCH, false);
|
||||
|
||||
@@ -476,28 +428,3 @@ export async function handleKickFromTeam(serverUrl: string, teamId: string) {
|
||||
logDebug('Failed to kick user from team', error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTeamMembersByIds(serverUrl: string, teamId: string, userIds: string[], fetchOnly?: boolean) {
|
||||
try {
|
||||
const client = NetworkManager.getClient(serverUrl);
|
||||
const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
const members = await client.getTeamMembersByIds(teamId, userIds);
|
||||
|
||||
if (!fetchOnly) {
|
||||
const roles = [];
|
||||
|
||||
for (const {roles: memberRoles} of members) {
|
||||
roles.push(...memberRoles.split(' '));
|
||||
}
|
||||
|
||||
fetchRolesIfNeeded(serverUrl, Array.from(new Set(roles)));
|
||||
|
||||
await operator.handleTeamMemberships({teamMemberships: members, prepareRecordsOnly: true});
|
||||
}
|
||||
|
||||
return {members};
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientError);
|
||||
return {error};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ export async function updateTermsOfServiceStatus(serverUrl: string, id: string,
|
||||
u.termsOfServiceId = '';
|
||||
}
|
||||
});
|
||||
operator.batchRecords([currentUser], 'updateTermsOfServiceStatus');
|
||||
operator.batchRecords([currentUser]);
|
||||
}
|
||||
return {resp};
|
||||
} catch (error) {
|
||||
|
||||
@@ -395,7 +395,7 @@ export const syncTeamThreads = async (serverUrl: string, teamId: string, prepare
|
||||
|
||||
if (!prepareRecordsOnly && models?.length) {
|
||||
try {
|
||||
await operator.batchRecords(models, 'syncTeamThreads');
|
||||
await operator.batchRecords(models);
|
||||
} catch (err) {
|
||||
if (__DEV__) {
|
||||
throw err;
|
||||
@@ -460,7 +460,7 @@ export const loadEarlierThreads = async (serverUrl: string, teamId: string, last
|
||||
|
||||
if (!prepareRecordsOnly && models?.length) {
|
||||
try {
|
||||
await operator.batchRecords(models, 'loadEarlierThreads');
|
||||
await operator.batchRecords(models);
|
||||
} catch (err) {
|
||||
if (__DEV__) {
|
||||
throw err;
|
||||
|
||||
@@ -74,7 +74,7 @@ export const fetchMe = async (serverUrl: string, fetchOnly = false): Promise<MyU
|
||||
}
|
||||
};
|
||||
|
||||
export async function fetchProfilesInChannel(serverUrl: string, channelId: string, excludeUserId?: string, options?: GetUsersOptions, fetchOnly = false): Promise<ProfilesInChannelRequest> {
|
||||
export async function fetchProfilesInChannel(serverUrl: string, channelId: string, excludeUserId?: string, fetchOnly = false): Promise<ProfilesInChannelRequest> {
|
||||
let client: Client;
|
||||
try {
|
||||
client = NetworkManager.getClient(serverUrl);
|
||||
@@ -83,7 +83,7 @@ export async function fetchProfilesInChannel(serverUrl: string, channelId: strin
|
||||
}
|
||||
|
||||
try {
|
||||
const users = await client.getProfilesInChannel(channelId, options);
|
||||
const users = await client.getProfilesInChannel(channelId);
|
||||
const uniqueUsers = Array.from(new Set(users));
|
||||
const filteredUsers = uniqueUsers.filter((u) => u.id !== excludeUserId);
|
||||
if (!fetchOnly) {
|
||||
@@ -102,13 +102,12 @@ export async function fetchProfilesInChannel(serverUrl: string, channelId: strin
|
||||
modelPromises.push(prepare);
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat(), 'fetchProfilesInChannel');
|
||||
await operator.batchRecords(models.flat());
|
||||
}
|
||||
}
|
||||
|
||||
return {channelId, users: filteredUsers};
|
||||
} catch (error) {
|
||||
logError('fetchProfilesInChannel', error);
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientError);
|
||||
return {channelId, error};
|
||||
}
|
||||
@@ -178,7 +177,7 @@ export async function fetchProfilesInGroupChannels(serverUrl: string, groupChann
|
||||
}
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat(), 'fetchProfilesInGroupChannels');
|
||||
await operator.batchRecords(models.flat());
|
||||
}
|
||||
|
||||
return {data};
|
||||
@@ -199,7 +198,7 @@ export async function fetchProfilesPerChannels(serverUrl: string, channelIds: st
|
||||
const data: ProfilesInChannelRequest[] = [];
|
||||
|
||||
for await (const cIds of channels) {
|
||||
const requests = cIds.map((id) => fetchProfilesInChannel(serverUrl, id, excludeUserId, undefined, true));
|
||||
const requests = cIds.map((id) => fetchProfilesInChannel(serverUrl, id, excludeUserId, true));
|
||||
const response = await Promise.all(requests);
|
||||
data.push(...response);
|
||||
}
|
||||
@@ -230,7 +229,7 @@ export async function fetchProfilesPerChannels(serverUrl: string, channelIds: st
|
||||
}
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat(), 'fetchProfilesPerChannels');
|
||||
await operator.batchRecords(models.flat());
|
||||
}
|
||||
|
||||
return {data};
|
||||
@@ -366,7 +365,7 @@ export async function fetchStatusByIds(serverUrl: string, userIds: string[], fet
|
||||
user.prepareStatus(status?.status || General.OFFLINE);
|
||||
}
|
||||
|
||||
await operator.batchRecords(users, 'fetchStatusByIds');
|
||||
await operator.batchRecords(users);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -531,7 +530,7 @@ export const fetchProfilesInTeam = async (serverUrl: string, teamId: string, pag
|
||||
}
|
||||
};
|
||||
|
||||
export const searchProfiles = async (serverUrl: string, term: string, options: SearchUserOptions, fetchOnly = false) => {
|
||||
export const searchProfiles = async (serverUrl: string, term: string, options: any = {}, fetchOnly = false) => {
|
||||
let client: Client;
|
||||
try {
|
||||
client = NetworkManager.getClient(serverUrl);
|
||||
@@ -564,7 +563,6 @@ export const searchProfiles = async (serverUrl: string, term: string, options: S
|
||||
|
||||
return {data: users};
|
||||
} catch (error) {
|
||||
logError('searchProfiles', error);
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientError);
|
||||
return {error};
|
||||
}
|
||||
@@ -641,7 +639,7 @@ export async function updateAllUsersSince(serverUrl: string, since: number, fetc
|
||||
modelsToBatch.push(...models);
|
||||
}
|
||||
|
||||
await operator.batchRecords(modelsToBatch, 'updateAllUsersSince');
|
||||
await operator.batchRecords(modelsToBatch);
|
||||
}
|
||||
} catch {
|
||||
// Do nothing
|
||||
@@ -677,7 +675,7 @@ export async function updateUsersNoLongerVisible(serverUrl: string, prepareRecor
|
||||
}
|
||||
}
|
||||
if (models.length && !prepareRecordsOnly) {
|
||||
serverDatabase.operator.batchRecords(models, 'updateUsersNoLongerVisible');
|
||||
serverDatabase.operator.batchRecords(models);
|
||||
}
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientError);
|
||||
@@ -917,7 +915,7 @@ export const fetchTeamAndChannelMembership = async (serverUrl: string, userId: s
|
||||
}
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat(), 'fetchTeamAndChannelMembership');
|
||||
await operator.batchRecords(models.flat());
|
||||
return {error: undefined};
|
||||
} catch (error) {
|
||||
return {error};
|
||||
|
||||
@@ -96,7 +96,7 @@ export async function handleCategoryOrderUpdatedEvent(serverUrl: string, msg: We
|
||||
c.sortOrder = order.findIndex(findOrder);
|
||||
});
|
||||
});
|
||||
await operator.batchRecords(categories, 'handleCategoryOrderUpdatedEvent');
|
||||
await operator.batchRecords(categories);
|
||||
}
|
||||
} catch (e) {
|
||||
logError('Category WS: handleCategoryOrderUpdatedEvent', e, msg);
|
||||
|
||||
@@ -57,7 +57,7 @@ export async function handleChannelCreatedEvent(serverUrl: string, msg: any) {
|
||||
}
|
||||
}
|
||||
}
|
||||
operator.batchRecords(models, 'handleChannelCreatedEvent');
|
||||
operator.batchRecords(models);
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
@@ -109,7 +109,7 @@ export async function handleChannelUpdatedEvent(serverUrl: string, msg: any) {
|
||||
if (infoModel.model) {
|
||||
models.push(...infoModel.model);
|
||||
}
|
||||
operator.batchRecords(models, 'handleChannelUpdatedEvent');
|
||||
operator.batchRecords(models);
|
||||
} catch {
|
||||
// Do nothing
|
||||
}
|
||||
@@ -165,7 +165,7 @@ export async function handleChannelMemberUpdatedEvent(serverUrl: string, msg: an
|
||||
if (rolesRequest.roles?.length) {
|
||||
models.push(...await operator.handleRole({roles: rolesRequest.roles, prepareRecordsOnly: true}));
|
||||
}
|
||||
operator.batchRecords(models, 'handleChannelMemberUpdatedEvent');
|
||||
operator.batchRecords(models);
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
@@ -235,7 +235,7 @@ export async function handleDirectAddedEvent(serverUrl: string, msg: WebSocketMe
|
||||
models.push(...userModels);
|
||||
}
|
||||
|
||||
operator.batchRecords(models, 'handleDirectAddedEvent');
|
||||
operator.batchRecords(models);
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
@@ -267,7 +267,7 @@ export async function handleUserAddedToChannelEvent(serverUrl: string, msg: any)
|
||||
const prepareModels = await Promise.all(prepare);
|
||||
const flattenedModels = prepareModels.flat();
|
||||
if (flattenedModels?.length > 0) {
|
||||
await operator.batchRecords(flattenedModels, 'handleUserAddedToChannelEvent - prepareMyChannelsForTeam');
|
||||
await operator.batchRecords(flattenedModels);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,7 +308,7 @@ export async function handleUserAddedToChannelEvent(serverUrl: string, msg: any)
|
||||
}
|
||||
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models, 'handleUserAddedToChannelEvent');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
await fetchChannelStats(serverUrl, channelId, false);
|
||||
@@ -356,7 +356,7 @@ export async function handleUserRemovedFromChannelEvent(serverUrl: string, msg:
|
||||
}
|
||||
}
|
||||
|
||||
operator.batchRecords(models, 'handleUserRemovedFromChannelEvent');
|
||||
operator.batchRecords(models);
|
||||
} catch (error) {
|
||||
logDebug('cannot handle user removed from channel websocket event', error);
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ async function doReconnect(serverUrl: string) {
|
||||
await handleEntryAfterLoadNavigation(serverUrl, teamData.memberships || [], chData?.memberships || [], currentTeam?.id || '', currentChannel?.id || '', initialTeamId, initialChannelId);
|
||||
|
||||
const dt = Date.now();
|
||||
await operator.batchRecords(models, 'doReconnect');
|
||||
await operator.batchRecords(models);
|
||||
logInfo('WEBSOCKET RECONNECT MODELS BATCHING TOOK', `${Date.now() - dt}ms`);
|
||||
setTeamLoading(serverUrl, false);
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ export async function handleNewPostEvent(serverUrl: string, msg: WebSocketMessag
|
||||
|
||||
models.push(...postModels);
|
||||
|
||||
operator.batchRecords(models, 'handleNewPostEvent');
|
||||
operator.batchRecords(models);
|
||||
}
|
||||
|
||||
export async function handlePostEdited(serverUrl: string, msg: WebSocketMessage) {
|
||||
@@ -220,7 +220,7 @@ export async function handlePostEdited(serverUrl: string, msg: WebSocketMessage)
|
||||
});
|
||||
models.push(...postModels);
|
||||
|
||||
operator.batchRecords(models, 'handlePostEdited');
|
||||
operator.batchRecords(models);
|
||||
}
|
||||
|
||||
export async function handlePostDeleted(serverUrl: string, msg: WebSocketMessage) {
|
||||
@@ -263,7 +263,7 @@ export async function handlePostDeleted(serverUrl: string, msg: WebSocketMessage
|
||||
}
|
||||
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models, 'handlePostDeleted');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
} catch {
|
||||
// Do nothing
|
||||
|
||||
@@ -107,7 +107,7 @@ async function handleSavePostAdded(serverUrl: string, preferences: PreferenceTyp
|
||||
return;
|
||||
}
|
||||
|
||||
const savedPosts = preferences.filter((p) => p.category === Preferences.CATEGORIES.SAVED_POST);
|
||||
const savedPosts = preferences.filter((p) => p.category === Preferences.CATEGORY_SAVED_POST);
|
||||
for await (const saved of savedPosts) {
|
||||
const post = await getPostById(database, saved.name);
|
||||
if (!post) {
|
||||
|
||||
@@ -66,7 +66,7 @@ export async function handleUserRoleUpdatedEvent(serverUrl: string, msg: WebSock
|
||||
models.push(user);
|
||||
}
|
||||
|
||||
await operator.batchRecords(models, 'handleUserRoleUpdatedEvent');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
export async function handleTeamMemberRoleUpdatedEvent(serverUrl: string, msg: WebSocketMessage): Promise<void> {
|
||||
@@ -118,7 +118,7 @@ export async function handleTeamMemberRoleUpdatedEvent(serverUrl: string, msg: W
|
||||
});
|
||||
models.push(...teamMembership);
|
||||
|
||||
await operator.batchRecords(models, 'handleTeamMemberRoleUpdatedEvent');
|
||||
await operator.batchRecords(models);
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@@ -162,5 +162,5 @@ const fetchAndStoreJoinedTeamInfo = async (serverUrl: string, operator: ServerDa
|
||||
}
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat(), 'fetchAndStoreJoinedTeamInfo');
|
||||
await operator.batchRecords(models.flat());
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ import DatabaseManager from '@database/manager';
|
||||
import {getTeammateNameDisplaySetting} from '@helpers/api/preference';
|
||||
import WebsocketManager from '@managers/websocket_manager';
|
||||
import {queryChannelsByTypes, queryUserChannelsByTypes} from '@queries/servers/channel';
|
||||
import {queryDisplayNamePreferences} from '@queries/servers/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {getConfig, getLicense} from '@queries/servers/system';
|
||||
import {getCurrentUser} from '@queries/servers/user';
|
||||
import {displayUsername} from '@utils/user';
|
||||
@@ -71,7 +71,7 @@ export async function handleUserUpdatedEvent(serverUrl: string, msg: WebSocketMe
|
||||
modelsToBatch.push(...userModel);
|
||||
|
||||
try {
|
||||
await operator.batchRecords(modelsToBatch, 'handleUserUpdatedEvent');
|
||||
await operator.batchRecords(modelsToBatch);
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
@@ -91,7 +91,7 @@ export async function handleUserTypingEvent(serverUrl: string, msg: WebSocketMes
|
||||
const {users, existingUsers} = await fetchUsersByIds(serverUrl, [msg.data.user_id]);
|
||||
const user = users?.[0] || existingUsers?.[0];
|
||||
|
||||
const namePreference = await queryDisplayNamePreferences(database, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const namePreference = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const teammateDisplayNameSetting = getTeammateNameDisplaySetting(namePreference, config.LockTeammateNameDisplay, config.TeammateNameDisplay, license);
|
||||
const currentUser = await getCurrentUser(database);
|
||||
const username = displayUsername(user, currentUser?.locale, teammateDisplayNameSetting);
|
||||
|
||||
@@ -3,15 +3,13 @@
|
||||
|
||||
import {buildQueryString} from '@utils/helpers';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientAppsMix {
|
||||
executeAppCall: <Res = unknown>(call: AppCallRequest, trackAsSubmit: boolean) => Promise<AppCallResponse<Res>>;
|
||||
getAppsBindings: (userID: string, channelID: string, teamID: string) => Promise<AppBinding[]>;
|
||||
}
|
||||
|
||||
const ClientApps = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
executeAppCall = async (call: AppCallRequest, trackAsSubmit: boolean) => {
|
||||
const ClientApps = (superclass: any) => class extends superclass {
|
||||
executeAppCall = async <Res = unknown>(call: AppCallRequest, trackAsSubmit: boolean): Promise<AppCallResponse<Res>> => {
|
||||
const callCopy = {
|
||||
...call,
|
||||
context: {
|
||||
|
||||
@@ -26,7 +26,6 @@ export default class ClientBase {
|
||||
requestHeaders: {[x: string]: string} = {};
|
||||
serverVersion = '';
|
||||
urlVersion = '/api/v4';
|
||||
enableLogging = false;
|
||||
|
||||
constructor(apiClient: APIClientInterface, serverUrl: string, bearerToken?: string, csrfToken?: string) {
|
||||
this.apiClient = apiClient;
|
||||
@@ -46,10 +45,6 @@ export default class ClientBase {
|
||||
}
|
||||
}
|
||||
|
||||
getBaseRoute() {
|
||||
return this.apiClient.baseUrl || '';
|
||||
}
|
||||
|
||||
getAbsoluteUrl(baseUrl?: string) {
|
||||
if (typeof baseUrl !== 'string' || !baseUrl.startsWith('/')) {
|
||||
return baseUrl;
|
||||
@@ -182,14 +177,10 @@ export default class ClientBase {
|
||||
return `${this.getEmojisRoute()}/${emojiId}`;
|
||||
}
|
||||
|
||||
getGlobalDataRetentionRoute() {
|
||||
getDataRetentionRoute() {
|
||||
return `${this.urlVersion}/data_retention`;
|
||||
}
|
||||
|
||||
getGranularDataRetentionRoute(userId: string) {
|
||||
return `${this.getUserRoute(userId)}/data_retention`;
|
||||
}
|
||||
|
||||
getRolesRoute() {
|
||||
return `${this.urlVersion}/roles`;
|
||||
}
|
||||
@@ -308,7 +299,7 @@ export default class ClientBase {
|
||||
}
|
||||
|
||||
if (response.ok) {
|
||||
return returnDataOnly ? (response.data || {}) : response;
|
||||
return returnDataOnly ? response.data : response;
|
||||
}
|
||||
|
||||
throw new ClientError(this.apiClient.baseUrl, {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientCategoriesMix {
|
||||
getCategories: (userId: string, teamId: string) => Promise<CategoriesWithOrder>;
|
||||
getCategoriesOrder: (userId: string, teamId: string) => Promise<string[]>;
|
||||
@@ -10,7 +8,7 @@ export interface ClientCategoriesMix {
|
||||
updateChannelCategories: (userId: string, teamId: string, categories: CategoryWithChannels[]) => Promise<CategoriesWithOrder>;
|
||||
}
|
||||
|
||||
const ClientCategories = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientCategories = (superclass: any) => class extends superclass {
|
||||
getCategories = async (userId: string, teamId: string) => {
|
||||
return this.doFetch(
|
||||
`${this.getCategoriesRoute(userId, teamId)}`,
|
||||
|
||||
@@ -5,8 +5,6 @@ import {buildQueryString} from '@utils/helpers';
|
||||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientChannelsMix {
|
||||
getAllChannels: (page?: number, perPage?: number, notAssociatedToGroup?: string, excludeDefaultChannels?: boolean, includeTotalCount?: boolean) => Promise<any>;
|
||||
createChannel: (channel: Channel) => Promise<Channel>;
|
||||
@@ -42,11 +40,9 @@ export interface ClientChannelsMix {
|
||||
searchChannels: (teamId: string, term: string) => Promise<Channel[]>;
|
||||
searchArchivedChannels: (teamId: string, term: string) => Promise<Channel[]>;
|
||||
searchAllChannels: (term: string, teamIds: string[], archivedOnly?: boolean) => Promise<Channel[]>;
|
||||
updateChannelMemberSchemeRoles: (channelId: string, userId: string, isSchemeUser: boolean, isSchemeAdmin: boolean) => Promise<any>;
|
||||
getMemberInChannel: (channelId: string, userId: string) => Promise<any>;
|
||||
}
|
||||
|
||||
const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientChannels = (superclass: any) => class extends superclass {
|
||||
getAllChannels = async (page = 0, perPage = PER_PAGE_DEFAULT, notAssociatedToGroup = '', excludeDefaultChannels = false, includeTotalCount = false) => {
|
||||
const queryData = {
|
||||
page,
|
||||
@@ -62,7 +58,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
createChannel = async (channel: Channel) => {
|
||||
this.analytics?.trackAPI('api_channels_create', {team_id: channel.team_id});
|
||||
this.analytics.trackAPI('api_channels_create', {team_id: channel.team_id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelsRoute()}`,
|
||||
@@ -71,7 +67,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
createDirectChannel = async (userIds: string[]) => {
|
||||
this.analytics?.trackAPI('api_channels_create_direct');
|
||||
this.analytics.trackAPI('api_channels_create_direct');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelsRoute()}/direct`,
|
||||
@@ -80,7 +76,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
createGroupChannel = async (userIds: string[]) => {
|
||||
this.analytics?.trackAPI('api_channels_create_group');
|
||||
this.analytics.trackAPI('api_channels_create_group');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelsRoute()}/group`,
|
||||
@@ -89,7 +85,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
deleteChannel = async (channelId: string) => {
|
||||
this.analytics?.trackAPI('api_channels_delete', {channel_id: channelId});
|
||||
this.analytics.trackAPI('api_channels_delete', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}`,
|
||||
@@ -98,7 +94,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
unarchiveChannel = async (channelId: string) => {
|
||||
this.analytics?.trackAPI('api_channels_unarchive', {channel_id: channelId});
|
||||
this.analytics.trackAPI('api_channels_unarchive', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/restore`,
|
||||
@@ -107,7 +103,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
updateChannel = async (channel: Channel) => {
|
||||
this.analytics?.trackAPI('api_channels_update', {channel_id: channel.id});
|
||||
this.analytics.trackAPI('api_channels_update', {channel_id: channel.id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channel.id)}`,
|
||||
@@ -120,7 +116,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
updateChannelPrivacy = async (channelId: string, privacy: any) => {
|
||||
this.analytics?.trackAPI('api_channels_update_privacy', {channel_id: channelId, privacy});
|
||||
this.analytics.trackAPI('api_channels_update_privacy', {channel_id: channelId, privacy});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/privacy`,
|
||||
@@ -129,7 +125,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
patchChannel = async (channelId: string, channelPatch: Partial<Channel>) => {
|
||||
this.analytics?.trackAPI('api_channels_patch', {channel_id: channelId});
|
||||
this.analytics.trackAPI('api_channels_patch', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/patch`,
|
||||
@@ -138,7 +134,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
updateChannelNotifyProps = async (props: ChannelNotifyProps & {channel_id: string; user_id: string}) => {
|
||||
this.analytics?.trackAPI('api_users_update_channel_notifications', {channel_id: props.channel_id});
|
||||
this.analytics.trackAPI('api_users_update_channel_notifications', {channel_id: props.channel_id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelMemberRoute(props.channel_id, props.user_id)}/notify_props`,
|
||||
@@ -147,7 +143,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
getChannel = async (channelId: string) => {
|
||||
this.analytics?.trackAPI('api_channel_get', {channel_id: channelId});
|
||||
this.analytics.trackAPI('api_channel_get', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}`,
|
||||
@@ -163,7 +159,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
getChannelByNameAndTeamName = async (teamName: string, channelName: string, includeDeleted = false) => {
|
||||
this.analytics?.trackAPI('api_channel_get_by_name_and_teamName', {channel_name: channelName, team_name: teamName, include_deleted: includeDeleted});
|
||||
this.analytics.trackAPI('api_channel_get_by_name_and_teamName', {channel_name: channelName, team_name: teamName, include_deleted: includeDeleted});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamNameRoute(teamName)}/channels/name/${channelName}?include_deleted=${includeDeleted}`,
|
||||
@@ -245,7 +241,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
addToChannel = async (userId: string, channelId: string, postRootId = '') => {
|
||||
this.analytics?.trackAPI('api_channels_add_member', {channel_id: channelId});
|
||||
this.analytics.trackAPI('api_channels_add_member', {channel_id: channelId});
|
||||
|
||||
const member = {user_id: userId, channel_id: channelId, post_root_id: postRootId};
|
||||
return this.doFetch(
|
||||
@@ -255,7 +251,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
removeFromChannel = async (userId: string, channelId: string) => {
|
||||
this.analytics?.trackAPI('api_channels_remove_member', {channel_id: channelId});
|
||||
this.analytics.trackAPI('api_channels_remove_member', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelMemberRoute(channelId, userId)}`,
|
||||
@@ -331,25 +327,6 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
{method: 'post', body},
|
||||
);
|
||||
};
|
||||
|
||||
// Update a channel member's scheme_admin/scheme_user properties. Typically
|
||||
// this should either be scheme_admin=false, scheme_user=true for ordinary
|
||||
// channel member, or scheme_admin=true, scheme_user=true for a channel
|
||||
// admin.
|
||||
updateChannelMemberSchemeRoles = (channelId: string, userId: string, isSchemeUser: boolean, isSchemeAdmin: boolean) => {
|
||||
const body = {scheme_user: isSchemeUser, scheme_admin: isSchemeAdmin};
|
||||
return this.doFetch(
|
||||
`${this.getChannelMembersRoute(channelId)}/${userId}/schemeRoles`,
|
||||
{method: 'put', body},
|
||||
);
|
||||
};
|
||||
|
||||
getMemberInChannel = (channelId: string, userId: string) => {
|
||||
return this.doFetch(
|
||||
`${this.getChannelMembersRoute(channelId)}/${userId}`,
|
||||
{method: 'get'},
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
export default ClientChannels;
|
||||
|
||||
@@ -5,8 +5,6 @@ import {buildQueryString} from '@utils/helpers';
|
||||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientEmojisMix {
|
||||
getCustomEmoji: (id: string) => Promise<CustomEmoji>;
|
||||
getCustomEmojiByName: (name: string) => Promise<CustomEmoji>;
|
||||
@@ -17,7 +15,7 @@ export interface ClientEmojisMix {
|
||||
autocompleteCustomEmoji: (name: string) => Promise<CustomEmoji[]>;
|
||||
}
|
||||
|
||||
const ClientEmojis = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientEmojis = (superclass: any) => class extends superclass {
|
||||
getCustomEmoji = async (id: string) => {
|
||||
return this.doFetch(
|
||||
`${this.getEmojisRoute()}/${id}`,
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
import {toMilliseconds} from '@utils/datetime';
|
||||
|
||||
import type ClientBase from './base';
|
||||
import type {ClientResponse, ClientResponseError, ProgressPromise, UploadRequestOptions} from '@mattermost/react-native-network-client';
|
||||
|
||||
export interface ClientFilesMix {
|
||||
@@ -23,7 +22,7 @@ export interface ClientFilesMix {
|
||||
searchFilesWithParams: (teamId: string, FileSearchParams: string) => Promise<FileSearchRequest>;
|
||||
}
|
||||
|
||||
const ClientFiles = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientFiles = (superclass: any) => class extends superclass {
|
||||
getFileUrl(fileId: string, timestamp: number) {
|
||||
let url = `${this.apiClient.baseUrl}${this.getFileRoute(fileId)}`;
|
||||
if (timestamp) {
|
||||
@@ -77,17 +76,13 @@ const ClientFiles = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
},
|
||||
timeoutInterval: toMilliseconds({minutes: 3}),
|
||||
};
|
||||
if (!file.localPath) {
|
||||
throw new Error('file does not have local path defined');
|
||||
}
|
||||
|
||||
const promise = this.apiClient.upload(url, file.localPath, options) as ProgressPromise<ClientResponse>;
|
||||
promise.progress!(onProgress).then(onComplete).catch(onError);
|
||||
return promise.cancel!;
|
||||
};
|
||||
|
||||
searchFilesWithParams = async (teamId: string, params: FileSearchParams) => {
|
||||
this.analytics?.trackAPI('api_files_search');
|
||||
this.analytics.trackAPI('api_files_search');
|
||||
const endpoint = teamId ? `${this.getTeamRoute(teamId)}/files/search` : `${this.getFilesRoute()}/search`;
|
||||
return this.doFetch(endpoint, {method: 'post', body: params});
|
||||
};
|
||||
|
||||
@@ -3,16 +3,8 @@
|
||||
|
||||
import {buildQueryString} from '@utils/helpers';
|
||||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
import ClientError from './error';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
type PoliciesResponse<T> = {
|
||||
policies: T[];
|
||||
total_count: number;
|
||||
}
|
||||
|
||||
export interface ClientGeneralMix {
|
||||
getOpenGraphMetadata: (url: string) => Promise<any>;
|
||||
ping: (deviceId?: string, timeoutInterval?: number) => Promise<any>;
|
||||
@@ -20,14 +12,12 @@ export interface ClientGeneralMix {
|
||||
getClientConfigOld: () => Promise<ClientConfig>;
|
||||
getClientLicenseOld: () => Promise<ClientLicense>;
|
||||
getTimezones: () => Promise<string[]>;
|
||||
getGlobalDataRetentionPolicy: () => Promise<GlobalDataRetentionPolicy>;
|
||||
getTeamDataRetentionPolicies: (userId: string, page?: number, perPage?: number) => Promise<PoliciesResponse<TeamDataRetentionPolicy>>;
|
||||
getChannelDataRetentionPolicies: (userId: string, page?: number, perPage?: number) => Promise<PoliciesResponse<ChannelDataRetentionPolicy>>;
|
||||
getDataRetentionPolicy: () => Promise<any>;
|
||||
getRolesByNames: (rolesNames: string[]) => Promise<Role[]>;
|
||||
getRedirectLocation: (urlParam: string) => Promise<Record<string, string>>;
|
||||
}
|
||||
|
||||
const ClientGeneral = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientGeneral = (superclass: any) => class extends superclass {
|
||||
getOpenGraphMetadata = async (url: string) => {
|
||||
return this.doFetch(
|
||||
`${this.urlVersion}/opengraph`,
|
||||
@@ -51,7 +41,7 @@ const ClientGeneral = <TBase extends Constructor<ClientBase>>(superclass: TBase)
|
||||
const url = `${this.urlVersion}/logs`;
|
||||
|
||||
if (!this.enableLogging) {
|
||||
throw new ClientError(this.apiClient.baseUrl, {
|
||||
throw new ClientError(this.client.baseUrl, {
|
||||
message: 'Logging disabled.',
|
||||
url,
|
||||
});
|
||||
@@ -84,23 +74,9 @@ const ClientGeneral = <TBase extends Constructor<ClientBase>>(superclass: TBase)
|
||||
);
|
||||
};
|
||||
|
||||
getGlobalDataRetentionPolicy = () => {
|
||||
getDataRetentionPolicy = () => {
|
||||
return this.doFetch(
|
||||
`${this.getGlobalDataRetentionRoute()}/policy`,
|
||||
{method: 'get'},
|
||||
);
|
||||
};
|
||||
|
||||
getTeamDataRetentionPolicies = (userId: string, page = 0, perPage = PER_PAGE_DEFAULT) => {
|
||||
return this.doFetch(
|
||||
`${this.getGranularDataRetentionRoute(userId)}/team_policies${buildQueryString({page, per_page: perPage})}`,
|
||||
{method: 'get'},
|
||||
);
|
||||
};
|
||||
|
||||
getChannelDataRetentionPolicies = (userId: string, page = 0, perPage = PER_PAGE_DEFAULT) => {
|
||||
return this.doFetch(
|
||||
`${this.getGranularDataRetentionRoute(userId)}/channel_policies${buildQueryString({page, per_page: perPage})}`,
|
||||
`${this.getDataRetentionRoute()}/policy`,
|
||||
{method: 'get'},
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,8 +5,6 @@ import {buildQueryString} from '@utils/helpers';
|
||||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientGroupsMix {
|
||||
getGroup: (id: string) => Promise<Group>;
|
||||
getGroups: (params: {query?: string; filterAllowReference?: boolean; page?: number; perPage?: number; since?: number; includeMemberCount?: boolean}) => Promise<Group[]>;
|
||||
@@ -18,7 +16,7 @@ export interface ClientGroupsMix {
|
||||
getAllTeamsAssociatedToGroup: (groupId: string, filterAllowReference?: boolean) => Promise<{groupTeams: GroupTeam[]}>;
|
||||
}
|
||||
|
||||
const ClientGroups = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientGroups = (superclass: any) => class extends superclass {
|
||||
getGroup = async (id: string) => {
|
||||
return this.doFetch(
|
||||
`${this.urlVersion}/groups/${id}`,
|
||||
|
||||
@@ -5,8 +5,6 @@ import {buildQueryString} from '@utils/helpers';
|
||||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientIntegrationsMix {
|
||||
getCommandsList: (teamId: string) => Promise<Command[]>;
|
||||
getCommandAutocompleteSuggestionsList: (userInput: string, teamId: string, channelId: string, rootId?: string) => Promise<AutocompleteSuggestion[]>;
|
||||
@@ -16,7 +14,7 @@ export interface ClientIntegrationsMix {
|
||||
submitInteractiveDialog: (data: DialogSubmission) => Promise<any>;
|
||||
}
|
||||
|
||||
const ClientIntegrations = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientIntegrations = (superclass: any) => class extends superclass {
|
||||
getCommandsList = async (teamId: string) => {
|
||||
return this.doFetch(
|
||||
`${this.getCommandsRoute()}?team_id=${teamId}`,
|
||||
@@ -39,7 +37,7 @@ const ClientIntegrations = <TBase extends Constructor<ClientBase>>(superclass: T
|
||||
};
|
||||
|
||||
executeCommand = async (command: string, commandArgs = {}) => {
|
||||
this.analytics?.trackAPI('api_integrations_used');
|
||||
this.analytics.trackAPI('api_integrations_used');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getCommandsRoute()}/execute`,
|
||||
@@ -48,7 +46,7 @@ const ClientIntegrations = <TBase extends Constructor<ClientBase>>(superclass: T
|
||||
};
|
||||
|
||||
addCommand = async (command: Command) => {
|
||||
this.analytics?.trackAPI('api_integrations_created');
|
||||
this.analytics.trackAPI('api_integrations_created');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getCommandsRoute()}`,
|
||||
@@ -57,7 +55,7 @@ const ClientIntegrations = <TBase extends Constructor<ClientBase>>(superclass: T
|
||||
};
|
||||
|
||||
submitInteractiveDialog = async (data: DialogSubmission) => {
|
||||
this.analytics?.trackAPI('api_interactive_messages_dialog_submitted');
|
||||
this.analytics.trackAPI('api_interactive_messages_dialog_submitted');
|
||||
return this.doFetch(
|
||||
`${this.urlVersion}/actions/dialogs/submit`,
|
||||
{method: 'post', body: data},
|
||||
|
||||
@@ -3,13 +3,11 @@
|
||||
|
||||
import {General} from '@constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientNPSMix {
|
||||
npsGiveFeedbackAction: () => Promise<Post>;
|
||||
}
|
||||
|
||||
const ClientNPS = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientNPS = (superclass: any) => class extends superclass {
|
||||
npsGiveFeedbackAction = async () => {
|
||||
return this.doFetch(
|
||||
`${this.getPluginRoute(General.NPS_PLUGIN_ID)}/api/v1/give_feedback`,
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientPluginsMix {
|
||||
getPluginsManifests: () => Promise<ClientPluginManifest[]>;
|
||||
}
|
||||
|
||||
const ClientPlugins = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientPlugins = (superclass: any) => class extends superclass {
|
||||
getPluginsManifests = async () => {
|
||||
return this.doFetch(
|
||||
`${this.getPluginsRoute()}/webapp`,
|
||||
|
||||
@@ -5,8 +5,6 @@ import {buildQueryString} from '@utils/helpers';
|
||||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientPostsMix {
|
||||
createPost: (post: Post) => Promise<Post>;
|
||||
updatePost: (post: Post) => Promise<Post>;
|
||||
@@ -33,12 +31,12 @@ export interface ClientPostsMix {
|
||||
doPostActionWithCookie: (postId: string, actionId: string, actionCookie: string, selectedOption?: string) => Promise<any>;
|
||||
}
|
||||
|
||||
const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientPosts = (superclass: any) => class extends superclass {
|
||||
createPost = async (post: Post) => {
|
||||
this.analytics?.trackAPI('api_posts_create', {channel_id: post.channel_id});
|
||||
this.analytics.trackAPI('api_posts_create', {channel_id: post.channel_id});
|
||||
|
||||
if (post.root_id != null && post.root_id !== '') {
|
||||
this.analytics?.trackAPI('api_posts_replied', {channel_id: post.channel_id});
|
||||
this.analytics.trackAPI('api_posts_replied', {channel_id: post.channel_id});
|
||||
}
|
||||
|
||||
return this.doFetch(
|
||||
@@ -48,7 +46,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
updatePost = async (post: Post) => {
|
||||
this.analytics?.trackAPI('api_posts_update', {channel_id: post.channel_id});
|
||||
this.analytics.trackAPI('api_posts_update', {channel_id: post.channel_id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getPostRoute(post.id)}`,
|
||||
@@ -64,7 +62,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
patchPost = async (postPatch: Partial<Post> & {id: string}) => {
|
||||
this.analytics?.trackAPI('api_posts_patch', {channel_id: postPatch.channel_id});
|
||||
this.analytics.trackAPI('api_posts_patch', {channel_id: postPatch.channel_id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getPostRoute(postPatch.id)}/patch`,
|
||||
@@ -73,7 +71,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
deletePost = async (postId: string) => {
|
||||
this.analytics?.trackAPI('api_posts_delete');
|
||||
this.analytics.trackAPI('api_posts_delete');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getPostRoute(postId)}`,
|
||||
@@ -81,7 +79,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
);
|
||||
};
|
||||
|
||||
getPostThread = (postId: string, options: FetchPaginatedThreadOptions) => {
|
||||
getPostThread = (postId: string, options: FetchPaginatedThreadOptions): Promise<PostResponse> => {
|
||||
const {
|
||||
fetchThreads = true,
|
||||
collapsedThreads = false,
|
||||
@@ -112,7 +110,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getPostsBefore = async (channelId: string, postId: string, page = 0, perPage = PER_PAGE_DEFAULT, collapsedThreads = false, collapsedThreadsExtended = false) => {
|
||||
this.analytics?.trackAPI('api_posts_get_before', {channel_id: channelId});
|
||||
this.analytics.trackAPI('api_posts_get_before', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/posts${buildQueryString({before: postId, page, per_page: perPage, collapsedThreads, collapsedThreadsExtended})}`,
|
||||
@@ -121,7 +119,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getPostsAfter = async (channelId: string, postId: string, page = 0, perPage = PER_PAGE_DEFAULT, collapsedThreads = false, collapsedThreadsExtended = false) => {
|
||||
this.analytics?.trackAPI('api_posts_get_after', {channel_id: channelId});
|
||||
this.analytics.trackAPI('api_posts_get_after', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/posts${buildQueryString({after: postId, page, per_page: perPage, collapsedThreads, collapsedThreadsExtended})}`,
|
||||
@@ -137,7 +135,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getSavedPosts = async (userId: string, channelId = '', teamId = '', page = 0, perPage = PER_PAGE_DEFAULT) => {
|
||||
this.analytics?.trackAPI('api_posts_get_flagged', {team_id: teamId});
|
||||
this.analytics.trackAPI('api_posts_get_flagged', {team_id: teamId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(userId)}/posts/flagged${buildQueryString({channel_id: channelId, team_id: teamId, page, per_page: perPage})}`,
|
||||
@@ -146,7 +144,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getPinnedPosts = async (channelId: string) => {
|
||||
this.analytics?.trackAPI('api_posts_get_pinned', {channel_id: channelId});
|
||||
this.analytics.trackAPI('api_posts_get_pinned', {channel_id: channelId});
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/pinned`,
|
||||
{method: 'get'},
|
||||
@@ -154,7 +152,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
markPostAsUnread = async (userId: string, postId: string) => {
|
||||
this.analytics?.trackAPI('api_post_set_unread_post');
|
||||
this.analytics.trackAPI('api_post_set_unread_post');
|
||||
|
||||
// collapsed_threads_supported is not based on user preferences but to know if "CLIENT" supports CRT
|
||||
const body = JSON.stringify({collapsed_threads_supported: true});
|
||||
@@ -166,7 +164,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
pinPost = async (postId: string) => {
|
||||
this.analytics?.trackAPI('api_posts_pin');
|
||||
this.analytics.trackAPI('api_posts_pin');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getPostRoute(postId)}/pin`,
|
||||
@@ -175,7 +173,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
unpinPost = async (postId: string) => {
|
||||
this.analytics?.trackAPI('api_posts_unpin');
|
||||
this.analytics.trackAPI('api_posts_unpin');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getPostRoute(postId)}/unpin`,
|
||||
@@ -184,7 +182,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
addReaction = async (userId: string, postId: string, emojiName: string) => {
|
||||
this.analytics?.trackAPI('api_reactions_save', {post_id: postId});
|
||||
this.analytics.trackAPI('api_reactions_save', {post_id: postId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getReactionsRoute()}`,
|
||||
@@ -193,7 +191,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
removeReaction = async (userId: string, postId: string, emojiName: string) => {
|
||||
this.analytics?.trackAPI('api_reactions_delete', {post_id: postId});
|
||||
this.analytics.trackAPI('api_reactions_delete', {post_id: postId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(userId)}/posts/${postId}/reactions/${emojiName}`,
|
||||
@@ -209,7 +207,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
searchPostsWithParams = async (teamId: string, params: PostSearchParams) => {
|
||||
this.analytics?.trackAPI('api_posts_search');
|
||||
this.analytics.trackAPI('api_posts_search');
|
||||
const endpoint = teamId ? `${this.getTeamRoute(teamId)}/posts/search` : `${this.getPostsRoute()}/search`;
|
||||
return this.doFetch(endpoint, {method: 'post', body: params});
|
||||
};
|
||||
@@ -224,9 +222,9 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
|
||||
doPostActionWithCookie = async (postId: string, actionId: string, actionCookie: string, selectedOption = '') => {
|
||||
if (selectedOption) {
|
||||
this.analytics?.trackAPI('api_interactive_messages_menu_selected');
|
||||
this.analytics.trackAPI('api_interactive_messages_menu_selected');
|
||||
} else {
|
||||
this.analytics?.trackAPI('api_interactive_messages_button_clicked');
|
||||
this.analytics.trackAPI('api_interactive_messages_button_clicked');
|
||||
}
|
||||
|
||||
const msg: any = {
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientPreferencesMix {
|
||||
savePreferences: (userId: string, preferences: PreferenceType[]) => Promise<any>;
|
||||
deletePreferences: (userId: string, preferences: PreferenceType[]) => Promise<any>;
|
||||
getMyPreferences: () => Promise<PreferenceType[]>;
|
||||
}
|
||||
|
||||
const ClientPreferences = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientPreferences = (superclass: any) => class extends superclass {
|
||||
savePreferences = async (userId: string, preferences: PreferenceType[]) => {
|
||||
this.analytics?.trackAPI('action_posts_flag');
|
||||
this.analytics.trackAPI('action_posts_flag');
|
||||
return this.doFetch(
|
||||
`${this.getPreferencesRoute(userId)}`,
|
||||
{method: 'put', body: preferences},
|
||||
@@ -26,7 +24,7 @@ const ClientPreferences = <TBase extends Constructor<ClientBase>>(superclass: TB
|
||||
};
|
||||
|
||||
deletePreferences = async (userId: string, preferences: PreferenceType[]) => {
|
||||
this.analytics?.trackAPI('action_posts_unflag');
|
||||
this.analytics.trackAPI('action_posts_unflag');
|
||||
return this.doFetch(
|
||||
`${this.getPreferencesRoute(userId)}/delete`,
|
||||
{method: 'post', body: preferences},
|
||||
|
||||
@@ -5,8 +5,6 @@ import {buildQueryString} from '@utils/helpers';
|
||||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientTeamsMix {
|
||||
createTeam: (team: Team) => Promise<Team>;
|
||||
deleteTeam: (teamId: string) => Promise<any>;
|
||||
@@ -20,19 +18,16 @@ export interface ClientTeamsMix {
|
||||
getMyTeamMembers: () => Promise<TeamMembership[]>;
|
||||
getTeamMembers: (teamId: string, page?: number, perPage?: number) => Promise<TeamMembership[]>;
|
||||
getTeamMember: (teamId: string, userId: string) => Promise<TeamMembership>;
|
||||
getTeamMembersByIds: (teamId: string, userIds: string[]) => Promise<TeamMembership[]>;
|
||||
addToTeam: (teamId: string, userId: string) => Promise<TeamMembership>;
|
||||
addUsersToTeamGracefully: (teamId: string, userIds: string[]) => Promise<TeamMemberWithError[]>;
|
||||
sendEmailInvitesToTeamGracefully: (teamId: string, emails: string[]) => Promise<TeamInviteWithError[]>;
|
||||
joinTeam: (inviteId: string) => Promise<TeamMembership>;
|
||||
removeFromTeam: (teamId: string, userId: string) => Promise<any>;
|
||||
getTeamStats: (teamId: string) => Promise<any>;
|
||||
getTeamIconUrl: (teamId: string, lastTeamIconUpdate: number) => string;
|
||||
}
|
||||
|
||||
const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientTeams = (superclass: any) => class extends superclass {
|
||||
createTeam = async (team: Team) => {
|
||||
this.analytics?.trackAPI('api_teams_create');
|
||||
this.analytics.trackAPI('api_teams_create');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamsRoute()}`,
|
||||
@@ -41,7 +36,7 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
deleteTeam = async (teamId: string) => {
|
||||
this.analytics?.trackAPI('api_teams_delete');
|
||||
this.analytics.trackAPI('api_teams_delete');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamRoute(teamId)}`,
|
||||
@@ -50,7 +45,7 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
updateTeam = async (team: Team) => {
|
||||
this.analytics?.trackAPI('api_teams_update_name', {team_id: team.id});
|
||||
this.analytics.trackAPI('api_teams_update_name', {team_id: team.id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamRoute(team.id)}`,
|
||||
@@ -59,7 +54,7 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
patchTeam = async (team: Partial<Team> & {id: string}) => {
|
||||
this.analytics?.trackAPI('api_teams_patch_name', {team_id: team.id});
|
||||
this.analytics.trackAPI('api_teams_patch_name', {team_id: team.id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamRoute(team.id)}/patch`,
|
||||
@@ -82,7 +77,7 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getTeamByName = async (teamName: string) => {
|
||||
this.analytics?.trackAPI('api_teams_get_team_by_name');
|
||||
this.analytics.trackAPI('api_teams_get_team_by_name');
|
||||
|
||||
return this.doFetch(
|
||||
this.getTeamNameRoute(teamName),
|
||||
@@ -125,15 +120,8 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
);
|
||||
};
|
||||
|
||||
getTeamMembersByIds = (teamId: string, userIds: string[]) => {
|
||||
return this.doFetch(
|
||||
`${this.getTeamMembersRoute(teamId)}/ids`,
|
||||
{method: 'post', body: userIds},
|
||||
);
|
||||
};
|
||||
|
||||
addToTeam = async (teamId: string, userId: string) => {
|
||||
this.analytics?.trackAPI('api_teams_invite_members', {team_id: teamId});
|
||||
this.analytics.trackAPI('api_teams_invite_members', {team_id: teamId});
|
||||
|
||||
const member = {user_id: userId, team_id: teamId};
|
||||
return this.doFetch(
|
||||
@@ -142,27 +130,6 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
);
|
||||
};
|
||||
|
||||
addUsersToTeamGracefully = (teamId: string, userIds: string[]) => {
|
||||
this.analytics?.trackAPI('api_teams_batch_add_members', {team_id: teamId, count: userIds.length});
|
||||
|
||||
const members: Array<{team_id: string; user_id: string}> = [];
|
||||
userIds.forEach((id) => members.push({team_id: teamId, user_id: id}));
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamMembersRoute(teamId)}/batch?graceful=true`,
|
||||
{method: 'post', body: members},
|
||||
);
|
||||
};
|
||||
|
||||
sendEmailInvitesToTeamGracefully = (teamId: string, emails: string[]) => {
|
||||
this.analytics?.trackAPI('api_teams_invite_members', {team_id: teamId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamRoute(teamId)}/invite/email?graceful=true`,
|
||||
{method: 'post', body: emails},
|
||||
);
|
||||
};
|
||||
|
||||
joinTeam = async (inviteId: string) => {
|
||||
const query = buildQueryString({invite_id: inviteId});
|
||||
return this.doFetch(
|
||||
@@ -172,7 +139,7 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
removeFromTeam = async (teamId: string, userId: string) => {
|
||||
this.analytics?.trackAPI('api_teams_remove_members', {team_id: teamId});
|
||||
this.analytics.trackAPI('api_teams_remove_members', {team_id: teamId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamMemberRoute(teamId, userId)}`,
|
||||
|
||||
@@ -5,8 +5,6 @@ import {buildQueryString, isMinimumServerVersion} from '@utils/helpers';
|
||||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientThreadsMix {
|
||||
getThreads: (userId: string, teamId: string, before?: string, after?: string, pageSize?: number, deleted?: boolean, unread?: boolean, since?: number, totalsOnly?: boolean, serverVersion?: string) => Promise<GetUserThreadsResponse>;
|
||||
getThread: (userId: string, teamId: string, threadId: string, extended?: boolean) => Promise<any>;
|
||||
@@ -16,7 +14,7 @@ export interface ClientThreadsMix {
|
||||
updateThreadFollow: (userId: string, teamId: string, threadId: string, state: boolean) => Promise<any>;
|
||||
}
|
||||
|
||||
const ClientThreads = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientThreads = (superclass: any) => class extends superclass {
|
||||
getThreads = async (userId: string, teamId: string, before = '', after = '', pageSize = PER_PAGE_DEFAULT, deleted = false, unread = false, since = 0, totalsOnly = false, serverVersion = '') => {
|
||||
const queryStringObj: Record<string, any> = {
|
||||
extended: 'true',
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientTosMix {
|
||||
updateMyTermsOfServiceStatus: (termsOfServiceId: string, accepted: boolean) => Promise<{status: string}>;
|
||||
getTermsOfService: () => Promise<TermsOfService>;
|
||||
}
|
||||
|
||||
const ClientTos = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientTos = (superclass: any) => class extends superclass {
|
||||
updateMyTermsOfServiceStatus = async (termsOfServiceId: string, accepted: boolean) => {
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute('me')}/terms_of_service`,
|
||||
|
||||
@@ -6,8 +6,6 @@ import {buildQueryString} from '@utils/helpers';
|
||||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientUsersMix {
|
||||
createUser: (user: UserProfile, token: string, inviteId: string) => Promise<UserProfile>;
|
||||
patchMe: (userPatch: Partial<UserProfile>) => Promise<UserProfile>;
|
||||
@@ -26,7 +24,7 @@ export interface ClientUsersMix {
|
||||
getProfilesInTeam: (teamId: string, page?: number, perPage?: number, sort?: string, options?: Record<string, any>) => Promise<UserProfile[]>;
|
||||
getProfilesNotInTeam: (teamId: string, groupConstrained: boolean, page?: number, perPage?: number) => Promise<UserProfile[]>;
|
||||
getProfilesWithoutTeam: (page?: number, perPage?: number, options?: Record<string, any>) => Promise<UserProfile[]>;
|
||||
getProfilesInChannel: (channelId: string, options?: GetUsersOptions) => Promise<UserProfile[]>;
|
||||
getProfilesInChannel: (channelId: string, page?: number, perPage?: number, sort?: string) => Promise<UserProfile[]>;
|
||||
getProfilesInGroupChannels: (channelsIds: string[]) => Promise<{[x: string]: UserProfile[]}>;
|
||||
getProfilesNotInChannel: (teamId: string, channelId: string, groupConstrained: boolean, page?: number, perPage?: number) => Promise<UserProfile[]>;
|
||||
getMe: () => Promise<UserProfile>;
|
||||
@@ -39,7 +37,7 @@ export interface ClientUsersMix {
|
||||
getSessions: (userId: string) => Promise<Session[]>;
|
||||
checkUserMfa: (loginId: string) => Promise<{mfa_required: boolean}>;
|
||||
attachDevice: (deviceId: string) => Promise<any>;
|
||||
searchUsers: (term: string, options: SearchUserOptions) => Promise<UserProfile[]>;
|
||||
searchUsers: (term: string, options: any) => Promise<UserProfile[]>;
|
||||
getStatusesByIds: (userIds: string[]) => Promise<UserStatus[]>;
|
||||
getStatus: (userId: string) => Promise<UserStatus>;
|
||||
updateStatus: (status: UserStatus) => Promise<UserStatus>;
|
||||
@@ -48,9 +46,9 @@ export interface ClientUsersMix {
|
||||
removeRecentCustomStatus: (customStatus: UserCustomStatus) => Promise<{status: string}>;
|
||||
}
|
||||
|
||||
const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientUsers = (superclass: any) => class extends superclass {
|
||||
createUser = async (user: UserProfile, token: string, inviteId: string) => {
|
||||
this.analytics?.trackAPI('api_users_create');
|
||||
this.analytics.trackAPI('api_users_create');
|
||||
|
||||
const queryParams: any = {};
|
||||
|
||||
@@ -76,7 +74,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
patchUser = async (userPatch: Partial<UserProfile> & {id: string}) => {
|
||||
this.analytics?.trackAPI('api_users_patch');
|
||||
this.analytics.trackAPI('api_users_patch');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(userPatch.id)}/patch`,
|
||||
@@ -85,7 +83,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
updateUser = async (user: UserProfile) => {
|
||||
this.analytics?.trackAPI('api_users_update');
|
||||
this.analytics.trackAPI('api_users_update');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(user.id)}`,
|
||||
@@ -94,7 +92,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
demoteUserToGuest = async (userId: string) => {
|
||||
this.analytics?.trackAPI('api_users_demote_user_to_guest');
|
||||
this.analytics.trackAPI('api_users_demote_user_to_guest');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(userId)}/demote`,
|
||||
@@ -103,7 +101,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getKnownUsers = async () => {
|
||||
this.analytics?.trackAPI('api_get_known_users');
|
||||
this.analytics.trackAPI('api_get_known_users');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/known`,
|
||||
@@ -112,7 +110,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
sendPasswordResetEmail = async (email: string) => {
|
||||
this.analytics?.trackAPI('api_users_send_password_reset');
|
||||
this.analytics.trackAPI('api_users_send_password_reset');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/password/reset/send`,
|
||||
@@ -121,7 +119,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
setDefaultProfileImage = async (userId: string) => {
|
||||
this.analytics?.trackAPI('api_users_set_default_profile_picture');
|
||||
this.analytics.trackAPI('api_users_set_default_profile_picture');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(userId)}/image`,
|
||||
@@ -130,10 +128,10 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
login = async (loginId: string, password: string, token = '', deviceId = '', ldapOnly = false) => {
|
||||
this.analytics?.trackAPI('api_users_login');
|
||||
this.analytics.trackAPI('api_users_login');
|
||||
|
||||
if (ldapOnly) {
|
||||
this.analytics?.trackAPI('api_users_login_ldap');
|
||||
this.analytics.trackAPI('api_users_login_ldap');
|
||||
}
|
||||
|
||||
const body: any = {
|
||||
@@ -161,7 +159,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
loginById = async (id: string, password: string, token = '', deviceId = '') => {
|
||||
this.analytics?.trackAPI('api_users_login');
|
||||
this.analytics.trackAPI('api_users_login');
|
||||
const body: any = {
|
||||
device_id: deviceId,
|
||||
id,
|
||||
@@ -183,7 +181,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
logout = async () => {
|
||||
this.analytics?.trackAPI('api_users_logout');
|
||||
this.analytics.trackAPI('api_users_logout');
|
||||
|
||||
const response = await this.doFetch(
|
||||
`${this.getUsersRoute()}/logout`,
|
||||
@@ -194,7 +192,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getProfiles = async (page = 0, perPage = PER_PAGE_DEFAULT, options = {}) => {
|
||||
this.analytics?.trackAPI('api_profiles_get');
|
||||
this.analytics.trackAPI('api_profiles_get');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}${buildQueryString({page, per_page: perPage, ...options})}`,
|
||||
@@ -203,7 +201,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getProfilesByIds = async (userIds: string[], options = {}) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_by_ids');
|
||||
this.analytics.trackAPI('api_profiles_get_by_ids');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/ids${buildQueryString(options)}`,
|
||||
@@ -212,7 +210,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getProfilesByUsernames = async (usernames: string[]) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_by_usernames');
|
||||
this.analytics.trackAPI('api_profiles_get_by_usernames');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/usernames`,
|
||||
@@ -221,7 +219,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getProfilesInTeam = async (teamId: string, page = 0, perPage = PER_PAGE_DEFAULT, sort = '', options = {}) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_in_team', {team_id: teamId, sort});
|
||||
this.analytics.trackAPI('api_profiles_get_in_team', {team_id: teamId, sort});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}${buildQueryString({...options, in_team: teamId, page, per_page: perPage, sort})}`,
|
||||
@@ -230,7 +228,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getProfilesNotInTeam = async (teamId: string, groupConstrained: boolean, page = 0, perPage = PER_PAGE_DEFAULT) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_not_in_team', {team_id: teamId, group_constrained: groupConstrained});
|
||||
this.analytics.trackAPI('api_profiles_get_not_in_team', {team_id: teamId, group_constrained: groupConstrained});
|
||||
|
||||
const queryStringObj: any = {not_in_team: teamId, page, per_page: perPage};
|
||||
if (groupConstrained) {
|
||||
@@ -244,7 +242,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getProfilesWithoutTeam = async (page = 0, perPage = PER_PAGE_DEFAULT, options = {}) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_without_team');
|
||||
this.analytics.trackAPI('api_profiles_get_without_team');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}${buildQueryString({...options, without_team: 1, page, per_page: perPage})}`,
|
||||
@@ -252,10 +250,10 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
);
|
||||
};
|
||||
|
||||
getProfilesInChannel = async (channelId: string, options: GetUsersOptions) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_in_channel', {channel_id: channelId});
|
||||
getProfilesInChannel = async (channelId: string, page = 0, perPage = PER_PAGE_DEFAULT, sort = '') => {
|
||||
this.analytics.trackAPI('api_profiles_get_in_channel', {channel_id: channelId});
|
||||
|
||||
const queryStringObj = {in_channel: channelId, ...options};
|
||||
const queryStringObj = {in_channel: channelId, page, per_page: perPage, sort};
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}${buildQueryString(queryStringObj)}`,
|
||||
{method: 'get'},
|
||||
@@ -263,7 +261,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getProfilesInGroupChannels = async (channelsIds: string[]) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_in_group_channels', {channelsIds});
|
||||
this.analytics.trackAPI('api_profiles_get_in_group_channels', {channelsIds});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/group_channels`,
|
||||
@@ -272,7 +270,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getProfilesNotInChannel = async (teamId: string, channelId: string, groupConstrained: boolean, page = 0, perPage = PER_PAGE_DEFAULT) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_not_in_channel', {team_id: teamId, channel_id: channelId, group_constrained: groupConstrained});
|
||||
this.analytics.trackAPI('api_profiles_get_not_in_channel', {team_id: teamId, channel_id: channelId, group_constrained: groupConstrained});
|
||||
|
||||
const queryStringObj: any = {in_team: teamId, not_in_channel: channelId, page, per_page: perPage};
|
||||
if (groupConstrained) {
|
||||
@@ -374,7 +372,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
searchUsers = async (term: string, options: any) => {
|
||||
this.analytics?.trackAPI('api_search_users');
|
||||
this.analytics.trackAPI('api_search_users');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/search`,
|
||||
|
||||
@@ -8,7 +8,7 @@ import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import {Preferences} from '@constants';
|
||||
import {queryAllCustomEmojis} from '@queries/servers/custom_emoji';
|
||||
import {queryEmojiPreferences} from '@queries/servers/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {observeConfigBooleanValue} from '@queries/servers/system';
|
||||
|
||||
import EmojiSuggestion from './emoji_suggestion';
|
||||
@@ -21,9 +21,12 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
const isCustomEmojisEnabled = observeConfigBooleanValue(database, 'EnableCustomEmoji');
|
||||
return {
|
||||
customEmojis: isCustomEmojisEnabled.pipe(
|
||||
switchMap((enabled) => (enabled ? queryAllCustomEmojis(database).observe() : of$(emptyEmojiList))),
|
||||
switchMap((enabled) => (enabled ?
|
||||
queryAllCustomEmojis(database).observe() :
|
||||
of$(emptyEmojiList)),
|
||||
),
|
||||
),
|
||||
skinTone: queryEmojiPreferences(database, Preferences.EMOJI_SKINTONE).
|
||||
skinTone: queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_EMOJI, Preferences.EMOJI_SKINTONE).
|
||||
observeWithColumns(['value']).pipe(
|
||||
switchMap((prefs) => of$(prefs?.[0]?.value ?? 'default')),
|
||||
),
|
||||
|
||||
@@ -545,7 +545,7 @@ export class ParsedCommand {
|
||||
this.incomplete += c;
|
||||
this.i++;
|
||||
if (escaped) {
|
||||
//TODO: handle \n, \t, other escaped chars https://mattermost.atlassian.net/browse/MM-43476
|
||||
//TODO: handle \n, \t, other escaped chars
|
||||
escaped = false;
|
||||
}
|
||||
break;
|
||||
@@ -734,7 +734,7 @@ export class ParsedCommand {
|
||||
this.incomplete += c;
|
||||
this.i++;
|
||||
if (escaped) {
|
||||
//TODO: handle \n, \t, other escaped chars https://mattermost.atlassian.net/browse/MM-43476
|
||||
//TODO: handle \n, \t, other escaped chars
|
||||
escaped = false;
|
||||
}
|
||||
break;
|
||||
@@ -1078,7 +1078,7 @@ export class AppCommandParser {
|
||||
}
|
||||
|
||||
// Add "Execute Current Command" suggestion
|
||||
// TODO get full text from SuggestionBox https://mattermost.atlassian.net/browse/MM-43477
|
||||
// TODO get full text from SuggestionBox
|
||||
const executableStates: string[] = [
|
||||
ParseState.EndCommand,
|
||||
ParseState.CommandSeparator,
|
||||
|
||||
@@ -19,7 +19,7 @@ import IntegrationsManager from '@managers/integrations_manager';
|
||||
import {AppCommandParser} from './app_command_parser/app_command_parser';
|
||||
import SlashSuggestionItem from './slash_suggestion_item';
|
||||
|
||||
// TODO: Remove when all below commands have been implemented https://mattermost.atlassian.net/browse/MM-43478
|
||||
// TODO: Remove when all below commands have been implemented
|
||||
const COMMANDS_TO_IMPLEMENT_LATER = ['collapse', 'expand', 'logout'];
|
||||
const NON_MOBILE_COMMANDS = ['shortcuts', 'search', 'settings'];
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ type Props = {
|
||||
testID?: string;
|
||||
}
|
||||
|
||||
const LeaveChannelLabel = ({canLeave, channelId, displayName, isOptionItem, type, testID}: Props) => {
|
||||
const LeaveChanelLabel = ({canLeave, channelId, displayName, isOptionItem, type, testID}: Props) => {
|
||||
const intl = useIntl();
|
||||
const serverUrl = useServerUrl();
|
||||
const isTablet = useIsTablet();
|
||||
@@ -183,4 +183,4 @@ const LeaveChannelLabel = ({canLeave, channelId, displayName, isOptionItem, type
|
||||
);
|
||||
};
|
||||
|
||||
export default LeaveChannelLabel;
|
||||
export default LeaveChanelLabel;
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
import {of as of$} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import {queryUsersById} from '@queries/servers/user';
|
||||
|
||||
import ManageMembersLabel from './manage_members_label';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
type OwnProps = WithDatabaseArgs & {
|
||||
isDefaultChannel: boolean;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
const enhanced = withObservables(['isDefaultChannel', 'userId'], ({isDefaultChannel, userId, database}: OwnProps) => {
|
||||
const users = queryUsersById(database, [userId]).observe();
|
||||
const canRemoveUser = users.pipe(
|
||||
switchMap((u) => {
|
||||
return of$(!isDefaultChannel || (isDefaultChannel && u[0].isGuest));
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
canRemoveUser,
|
||||
};
|
||||
});
|
||||
|
||||
export default withDatabase(enhanced(ManageMembersLabel));
|
||||
@@ -1,152 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback} from 'react';
|
||||
import {defineMessages, useIntl} from 'react-intl';
|
||||
import {Alert, DeviceEventEmitter} from 'react-native';
|
||||
|
||||
import {fetchChannelStats, removeMemberFromChannel, updateChannelMemberSchemeRoles} from '@actions/remote/channel';
|
||||
import OptionItem from '@components/option_item';
|
||||
import {Events, Members} from '@constants';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {t} from '@i18n';
|
||||
import {dismissBottomSheet} from '@screens/navigation';
|
||||
import {alertErrorWithFallback} from '@utils/draft';
|
||||
|
||||
import type {ManageOptionsTypes} from '@constants/members';
|
||||
|
||||
const {MAKE_CHANNEL_ADMIN, MAKE_CHANNEL_MEMBER, REMOVE_USER} = Members.ManageOptions;
|
||||
|
||||
const messages = defineMessages({
|
||||
role_change_error: {
|
||||
id: t('mobile.manage_members.change_role.error'),
|
||||
defaultMessage: 'An error occurred while trying to update the role. Please check your connection and try again.',
|
||||
},
|
||||
make_channel_admin: {
|
||||
id: t('mobile.manage_members.make_channel_admin'),
|
||||
defaultMessage: 'Make Channel Admin',
|
||||
},
|
||||
make_channel_member: {
|
||||
id: t('mobile.manage_members.make_channel_member'),
|
||||
defaultMessage: 'Make Channel Member',
|
||||
},
|
||||
remove_title: {
|
||||
id: t('mobile.manage_members.remove_member'),
|
||||
defaultMessage: 'Remove From Channel',
|
||||
},
|
||||
remove_message: {
|
||||
id: t('mobile.manage_members.message'),
|
||||
defaultMessage: 'Are you sure you want to remove the selected member from the channel?',
|
||||
},
|
||||
remove_cancel: {
|
||||
id: t('mobile.manage_members.cancel'),
|
||||
defaultMessage: 'Cancel',
|
||||
},
|
||||
remove_confirm: {
|
||||
id: t('mobile.manage_members.remove'),
|
||||
defaultMessage: 'Remove',
|
||||
},
|
||||
});
|
||||
|
||||
type Props = {
|
||||
canRemoveUser: boolean;
|
||||
channelId: string;
|
||||
manageOption: ManageOptionsTypes;
|
||||
testID?: string;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
const ManageMembersLabel = ({canRemoveUser, channelId, manageOption, testID, userId}: Props) => {
|
||||
const intl = useIntl();
|
||||
const {formatMessage} = intl;
|
||||
const serverUrl = useServerUrl();
|
||||
|
||||
const handleRemoveUser = useCallback(async () => {
|
||||
removeMemberFromChannel(serverUrl, channelId, userId);
|
||||
fetchChannelStats(serverUrl, channelId, false);
|
||||
await dismissBottomSheet();
|
||||
DeviceEventEmitter.emit(Events.REMOVE_USER_FROM_CHANNEL, userId);
|
||||
}, [channelId, serverUrl, userId]);
|
||||
|
||||
const removeFromChannel = useCallback(() => {
|
||||
Alert.alert(
|
||||
formatMessage(messages.remove_title),
|
||||
formatMessage(messages.remove_message),
|
||||
[{
|
||||
text: formatMessage(messages.remove_cancel),
|
||||
style: 'cancel',
|
||||
}, {
|
||||
text: formatMessage(messages.remove_confirm),
|
||||
style: 'destructive',
|
||||
onPress: handleRemoveUser,
|
||||
}], {cancelable: false},
|
||||
);
|
||||
}, [formatMessage, handleRemoveUser]);
|
||||
|
||||
const updateChannelMemberSchemeRole = useCallback(async (schemeAdmin: boolean) => {
|
||||
const result = await updateChannelMemberSchemeRoles(serverUrl, channelId, userId, true, schemeAdmin);
|
||||
if (result.error) {
|
||||
alertErrorWithFallback(intl, result.error, messages.role_change_error);
|
||||
}
|
||||
await dismissBottomSheet();
|
||||
DeviceEventEmitter.emit(Events.MANAGE_USER_CHANGE_ROLE, {userId, schemeAdmin});
|
||||
}, [channelId, userId, intl, serverUrl]);
|
||||
|
||||
const onAction = useCallback(() => {
|
||||
switch (manageOption) {
|
||||
case REMOVE_USER:
|
||||
removeFromChannel();
|
||||
break;
|
||||
case MAKE_CHANNEL_ADMIN:
|
||||
updateChannelMemberSchemeRole(true);
|
||||
break;
|
||||
case MAKE_CHANNEL_MEMBER:
|
||||
updateChannelMemberSchemeRole(false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}, [manageOption, removeFromChannel, updateChannelMemberSchemeRole]);
|
||||
|
||||
let actionText;
|
||||
let icon;
|
||||
let isDestructive = false;
|
||||
switch (manageOption) {
|
||||
case REMOVE_USER:
|
||||
actionText = (formatMessage(messages.remove_title));
|
||||
icon = 'trash-can-outline';
|
||||
isDestructive = true;
|
||||
break;
|
||||
case MAKE_CHANNEL_ADMIN:
|
||||
actionText = formatMessage(messages.make_channel_admin);
|
||||
icon = 'account-outline';
|
||||
break;
|
||||
case MAKE_CHANNEL_MEMBER:
|
||||
actionText = formatMessage(messages.make_channel_member);
|
||||
icon = 'account-outline';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (manageOption === REMOVE_USER && !canRemoveUser) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!actionText) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<OptionItem
|
||||
action={onAction}
|
||||
destructive={isDestructive}
|
||||
icon={icon}
|
||||
label={actionText}
|
||||
testID={testID}
|
||||
type='default'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ManageMembersLabel;
|
||||
@@ -12,8 +12,9 @@ import {switchMap} from 'rxjs/operators';
|
||||
import FormattedDate from '@components/formatted_date';
|
||||
import FormattedText from '@components/formatted_text';
|
||||
import FormattedTime from '@components/formatted_time';
|
||||
import {getDisplayNamePreferenceAsBool} from '@helpers/api/preference';
|
||||
import {queryDisplayNamePreferences} from '@queries/servers/preference';
|
||||
import {Preferences} from '@constants';
|
||||
import {getPreferenceAsBool} from '@helpers/api/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {observeCurrentUser} from '@queries/servers/user';
|
||||
import {getCurrentMomentForTimezone} from '@utils/helpers';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
@@ -133,10 +134,10 @@ const CustomStatusExpiry = ({currentUser, isMilitaryTime, showPrefix, showTimeCo
|
||||
|
||||
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => ({
|
||||
currentUser: observeCurrentUser(database),
|
||||
isMilitaryTime: queryDisplayNamePreferences(database).
|
||||
isMilitaryTime: queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS).
|
||||
observeWithColumns(['value']).pipe(
|
||||
switchMap(
|
||||
(preferences) => of$(getDisplayNamePreferenceAsBool(preferences, 'use_military_time')),
|
||||
(preferences) => of$(getPreferenceAsBool(preferences, Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', false)),
|
||||
),
|
||||
),
|
||||
}));
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
import {of as of$, from as from$} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
import {combineLatest, of as of$, from as from$} from 'rxjs';
|
||||
import {map, switchMap} from 'rxjs/operators';
|
||||
|
||||
import {queryFilesForPost} from '@queries/servers/file';
|
||||
import {observeCanDownloadFiles, observeConfigBooleanValue} from '@queries/servers/system';
|
||||
import {observeConfigBooleanValue, observeLicense} from '@queries/servers/system';
|
||||
import {fileExists} from '@utils/file';
|
||||
|
||||
import Files from './files';
|
||||
@@ -37,14 +37,23 @@ const filesLocalPathValidation = async (files: FileModel[], authorId: string) =>
|
||||
};
|
||||
|
||||
const enhance = withObservables(['post'], ({database, post}: EnhanceProps) => {
|
||||
const enableMobileFileDownload = observeConfigBooleanValue(database, 'EnableMobileFileDownload');
|
||||
const publicLinkEnabled = observeConfigBooleanValue(database, 'EnablePublicLink');
|
||||
|
||||
const complianceDisabled = observeLicense(database).pipe(
|
||||
switchMap((lcs) => of$(lcs?.IsLicensed === 'false' || lcs?.Compliance === 'false')),
|
||||
);
|
||||
|
||||
const canDownloadFiles = combineLatest([enableMobileFileDownload, complianceDisabled]).pipe(
|
||||
map(([download, compliance]) => compliance || download),
|
||||
);
|
||||
|
||||
const filesInfo = queryFilesForPost(database, post.id).observeWithColumns(['local_path']).pipe(
|
||||
switchMap((fs) => from$(filesLocalPathValidation(fs, post.userId))),
|
||||
);
|
||||
|
||||
return {
|
||||
canDownloadFiles: observeCanDownloadFiles(database),
|
||||
canDownloadFiles,
|
||||
postId: of$(post.id),
|
||||
publicLinkEnabled,
|
||||
filesInfo,
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import * as React from 'react';
|
||||
import Svg, {Path} from 'react-native-svg';
|
||||
|
||||
function AlertSvgComponent() {
|
||||
return (
|
||||
<Svg
|
||||
width={55}
|
||||
height={55}
|
||||
viewBox='0 0 55 55'
|
||||
fill='none'
|
||||
>
|
||||
<Path
|
||||
d='M4.43715 54.2949C1.46653 54.2949 0.12055 52.1536 1.44612 49.5365L25.4558 2.37353C26.8154 -0.236815 28.9566 -0.236815 30.289 2.37353L54.292 49.5365C55.6515 52.1468 54.292 54.2949 51.3009 54.2949H4.43715Z'
|
||||
fill='#FFBC1F'
|
||||
/>
|
||||
<Path
|
||||
d='M24.1032 19.8165L26.5708 36.3963C26.5946 36.7253 26.7422 37.0331 26.9837 37.2578C27.2252 37.4824 27.5428 37.6073 27.8726 37.6073C28.2025 37.6073 28.5201 37.4824 28.7616 37.2578C29.0031 37.0331 29.1506 36.7253 29.1744 36.3963L31.642 19.8165C32.0907 13.3518 23.6478 13.3518 24.1032 19.8165Z'
|
||||
fill='#2D3039'
|
||||
/>
|
||||
<Path
|
||||
d='M27.8688 39.3942C28.6161 39.3955 29.3461 39.6183 29.9668 40.0344C30.5874 40.4506 31.0708 41.0413 31.3559 41.7321C31.6409 42.4228 31.7147 43.1825 31.5681 43.9153C31.4215 44.648 31.061 45.3208 30.5322 45.8487C30.0033 46.3766 29.3299 46.7359 28.5969 46.8812C27.8639 47.0265 27.1043 46.9512 26.4141 46.6649C25.7239 46.3787 25.134 45.8943 24.719 45.2729C24.304 44.6515 24.0825 43.921 24.0825 43.1737C24.0825 42.6768 24.1804 42.1848 24.3708 41.7258C24.5612 41.2668 24.8402 40.8498 25.1919 40.4988C25.5436 40.1477 25.9611 39.8695 26.4204 39.6799C26.8798 39.4904 27.3719 39.3933 27.8688 39.3942Z'
|
||||
fill='#2D3039'
|
||||
/>
|
||||
</Svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default AlertSvgComponent;
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import * as React from 'react';
|
||||
import Svg, {G, Path, Defs, ClipPath, Rect} from 'react-native-svg';
|
||||
|
||||
function ErrorSvgComponent() {
|
||||
return (
|
||||
<Svg
|
||||
width={46}
|
||||
height={45}
|
||||
viewBox='0 0 46 45'
|
||||
fill='none'
|
||||
>
|
||||
<G clipPath='url(#clip0_1304_35713)'>
|
||||
<Path
|
||||
d='M45.2126 6.63077L38.8691 0.287231L23.0033 16.153L7.13065 0.287231L0.787109 6.63077L16.6529 22.5035L0.787109 38.3692L7.13065 44.7128L23.0033 28.847L38.8691 44.7128L45.2126 38.3692L29.3469 22.5035L45.2126 6.63077Z'
|
||||
fill='#D24B4E'
|
||||
/>
|
||||
</G>
|
||||
<Defs>
|
||||
<ClipPath id='clip0_1304_35713'>
|
||||
<Rect
|
||||
width='44.4255'
|
||||
height='44.4255'
|
||||
fill='white'
|
||||
transform='translate(0.787109 0.287231)'
|
||||
/>
|
||||
</ClipPath>
|
||||
</Defs>
|
||||
</Svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default ErrorSvgComponent;
|
||||
@@ -1,22 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import * as React from 'react';
|
||||
import Svg, {Path} from 'react-native-svg';
|
||||
|
||||
function SuccessSvgComponent() {
|
||||
return (
|
||||
<Svg
|
||||
width={46}
|
||||
height={47}
|
||||
viewBox='0 0 46 47'
|
||||
fill='none'
|
||||
>
|
||||
<Path
|
||||
d='M41.1767 0.776611L13.0005 31.7625L4.82284 25.5642H0.276367L13.0005 46.2234L45.7232 0.776611H41.1767Z'
|
||||
fill='#3DB887'
|
||||
/>
|
||||
</Svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default SuccessSvgComponent;
|
||||
@@ -3,14 +3,14 @@
|
||||
|
||||
import React, {useCallback, useEffect, useState} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {DeviceEventEmitter} from 'react-native';
|
||||
import {Alert, DeviceEventEmitter} from 'react-native';
|
||||
|
||||
import {getChannelTimezones} from '@actions/remote/channel';
|
||||
import {executeCommand, handleGotoLocation} from '@actions/remote/command';
|
||||
import {createPost} from '@actions/remote/post';
|
||||
import {handleReactionToLatestPost} from '@actions/remote/reactions';
|
||||
import {setStatus} from '@actions/remote/user';
|
||||
import {handleCallsSlashCommand} from '@calls/actions/calls';
|
||||
import {canEndCall, endCall, getEndCallMessage} from '@calls/actions/calls';
|
||||
import {Events, Screens} from '@constants';
|
||||
import {PostPriorityType} from '@constants/post';
|
||||
import {NOTIFY_ALL_MEMBERS} from '@constants/post_draft';
|
||||
@@ -23,6 +23,7 @@ import {confirmOutOfOfficeDisabled} from '@utils/user';
|
||||
|
||||
import DraftInput from '../draft_input';
|
||||
|
||||
import type ClientError from '@client/rest/error';
|
||||
import type CustomEmojiModel from '@typings/database/models/servers/custom_emoji';
|
||||
|
||||
type Props = {
|
||||
@@ -146,19 +147,54 @@ export default function SendHandler({
|
||||
DraftUtils.alertChannelWideMention(intl, notifyAllMessage, doSubmitMessage, cancel);
|
||||
}, [intl, isTimezoneEnabled, channelTimezoneCount, doSubmitMessage]);
|
||||
|
||||
const handleEndCall = useCallback(async () => {
|
||||
const hasPermissions = await canEndCall(serverUrl, channelId);
|
||||
|
||||
if (!hasPermissions) {
|
||||
Alert.alert(
|
||||
intl.formatMessage({
|
||||
id: 'mobile.calls_end_permission_title',
|
||||
defaultMessage: 'Error',
|
||||
}),
|
||||
intl.formatMessage({
|
||||
id: 'mobile.calls_end_permission_msg',
|
||||
defaultMessage: 'You don\'t have permission to end the call. Please ask the call owner to end the call.',
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
const message = await getEndCallMessage(serverUrl, channelId, currentUserId, intl);
|
||||
const title = intl.formatMessage({id: 'mobile.calls_end_call_title', defaultMessage: 'End call'});
|
||||
|
||||
Alert.alert(
|
||||
title,
|
||||
message,
|
||||
[
|
||||
{
|
||||
text: intl.formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'}),
|
||||
},
|
||||
{
|
||||
text: title,
|
||||
onPress: async () => {
|
||||
try {
|
||||
await endCall(serverUrl, channelId);
|
||||
} catch (e) {
|
||||
const err = (e as ClientError).message || 'unable to complete command, see server logs';
|
||||
Alert.alert('Error', `Error: ${err}`);
|
||||
}
|
||||
},
|
||||
style: 'cancel',
|
||||
},
|
||||
],
|
||||
);
|
||||
}, [serverUrl, channelId, currentUserId, intl]);
|
||||
|
||||
const sendCommand = useCallback(async () => {
|
||||
if (value.trim().startsWith('/call')) {
|
||||
const {handled, error} = await handleCallsSlashCommand(value.trim(), serverUrl, channelId, currentUserId, intl);
|
||||
if (handled) {
|
||||
setSendingMessage(false);
|
||||
clearDraft();
|
||||
return;
|
||||
}
|
||||
if (error) {
|
||||
setSendingMessage(false);
|
||||
DraftUtils.alertSlashCommandFailed(intl, error);
|
||||
return;
|
||||
}
|
||||
if (value.trim() === '/call end') {
|
||||
await handleEndCall();
|
||||
setSendingMessage(false);
|
||||
clearDraft();
|
||||
return;
|
||||
}
|
||||
|
||||
const status = DraftUtils.getStatusFromSlashCommand(value);
|
||||
@@ -190,7 +226,7 @@ export default function SendHandler({
|
||||
if (data?.goto_location && !value.startsWith('/leave')) {
|
||||
handleGotoLocation(serverUrl, intl, data.goto_location);
|
||||
}
|
||||
}, [userIsOutOfOffice, currentUserId, intl, value, serverUrl, channelId, rootId]);
|
||||
}, [userIsOutOfOffice, currentUserId, intl, value, serverUrl, channelId, rootId, handleEndCall]);
|
||||
|
||||
const sendMessage = useCallback(() => {
|
||||
const notificationsToChannel = enableConfirmNotificationsToChannel && useChannelMentions;
|
||||
|
||||
@@ -6,8 +6,8 @@ import withObservables from '@nozbe/with-observables';
|
||||
import {of as of$, combineLatest} from 'rxjs';
|
||||
|
||||
import {Preferences} from '@constants';
|
||||
import {getDisplayNamePreferenceAsBool} from '@helpers/api/preference';
|
||||
import {queryDisplayNamePreferences} from '@queries/servers/preference';
|
||||
import {getPreferenceAsBool} from '@helpers/api/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {observeConfigBooleanValue} from '@queries/servers/system';
|
||||
|
||||
import Opengraph from './opengraph';
|
||||
@@ -22,10 +22,10 @@ const enhance = withObservables(
|
||||
}
|
||||
|
||||
const linkPreviewsConfig = observeConfigBooleanValue(database, 'EnableLinkPreviews');
|
||||
const linkPreviewPreference = queryDisplayNamePreferences(database, Preferences.LINK_PREVIEW_DISPLAY).
|
||||
const linkPreviewPreference = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.LINK_PREVIEW_DISPLAY).
|
||||
observeWithColumns(['value']);
|
||||
const showLinkPreviews = combineLatest([linkPreviewsConfig, linkPreviewPreference], (cfg, pref) => {
|
||||
const previewsEnabled = getDisplayNamePreferenceAsBool(pref, Preferences.LINK_PREVIEW_DISPLAY, true);
|
||||
const previewsEnabled = getPreferenceAsBool(pref, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.LINK_PREVIEW_DISPLAY, true);
|
||||
return of$(previewsEnabled && cfg);
|
||||
});
|
||||
|
||||
|
||||
@@ -6,9 +6,10 @@ import withObservables from '@nozbe/with-observables';
|
||||
import {of as of$} from 'rxjs';
|
||||
import {map, switchMap} from 'rxjs/operators';
|
||||
|
||||
import {getDisplayNamePreferenceAsBool} from '@helpers/api/preference';
|
||||
import {Preferences} from '@constants';
|
||||
import {getPreferenceAsBool} from '@helpers/api/preference';
|
||||
import {observePost, observePostAuthor, queryPostReplies} from '@queries/servers/post';
|
||||
import {queryDisplayNamePreferences} from '@queries/servers/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {observeConfigBooleanValue} from '@queries/servers/system';
|
||||
import {observeTeammateNameDisplay, observeUser} from '@queries/servers/user';
|
||||
|
||||
@@ -25,12 +26,12 @@ type HeaderInputProps = {
|
||||
const withHeaderProps = withObservables(
|
||||
['post', 'differentThreadSequence'],
|
||||
({post, database, differentThreadSequence}: WithDatabaseArgs & HeaderInputProps) => {
|
||||
const preferences = queryDisplayNamePreferences(database).
|
||||
const preferences = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS).
|
||||
observeWithColumns(['value']);
|
||||
const author = observePostAuthor(database, post);
|
||||
const enablePostUsernameOverride = observeConfigBooleanValue(database, 'EnablePostUsernameOverride');
|
||||
const isTimezoneEnabled = observeConfigBooleanValue(database, 'ExperimentalTimezone');
|
||||
const isMilitaryTime = preferences.pipe(map((prefs) => getDisplayNamePreferenceAsBool(prefs, 'use_military_time')));
|
||||
const isMilitaryTime = preferences.pipe(map((prefs) => getPreferenceAsBool(prefs, Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', false)));
|
||||
const teammateNameDisplay = observeTeammateNameDisplay(database);
|
||||
const commentCount = queryPostReplies(database, post.rootId || post.id).observeCount();
|
||||
const isCustomStatusEnabled = observeConfigBooleanValue(database, 'EnableCustomUserStatuses');
|
||||
|
||||
@@ -102,7 +102,7 @@ const withPost = withObservables(
|
||||
const isEphemeral = of$(isPostEphemeral(post));
|
||||
|
||||
if (post.props?.add_channel_member && isPostEphemeral(post)) {
|
||||
isPostAddChannelMember = observeCanManageChannelMembers(database, post.channelId, currentUser);
|
||||
isPostAddChannelMember = observeCanManageChannelMembers(database, post, currentUser);
|
||||
}
|
||||
|
||||
let highlightReplyBar = of$(false);
|
||||
|
||||
@@ -6,8 +6,9 @@ import withObservables from '@nozbe/with-observables';
|
||||
import {of as of$} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import {Preferences} from '@constants';
|
||||
import {observePost, queryPostReplies} from '@queries/servers/post';
|
||||
import {querySavedPostsPreferences} from '@queries/servers/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
|
||||
import ThreadOverview from './thread_overview';
|
||||
|
||||
@@ -18,7 +19,7 @@ const enhanced = withObservables(
|
||||
({database, rootId}: WithDatabaseArgs & {rootId: string}) => {
|
||||
return {
|
||||
rootPost: observePost(database, rootId),
|
||||
isSaved: querySavedPostsPreferences(database, rootId).
|
||||
isSaved: queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SAVED_POST, rootId).
|
||||
observeWithColumns(['value']).
|
||||
pipe(
|
||||
switchMap((pref) => of$(Boolean(pref[0]?.value === 'true'))),
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback} from 'react';
|
||||
import {Text, TouchableOpacity, View} from 'react-native';
|
||||
import Animated, {FadeIn, FadeOut} from 'react-native-reanimated';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
type SelectedChipProps = {
|
||||
id: string;
|
||||
text: string;
|
||||
extra?: React.ReactNode;
|
||||
onRemove: (id: string) => void;
|
||||
testID?: string;
|
||||
}
|
||||
|
||||
export const USER_CHIP_HEIGHT = 32;
|
||||
export const USER_CHIP_BOTTOM_MARGIN = 8;
|
||||
const FADE_DURATION = 100;
|
||||
|
||||
const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'row',
|
||||
borderRadius: 16,
|
||||
height: USER_CHIP_HEIGHT,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.08),
|
||||
marginBottom: USER_CHIP_BOTTOM_MARGIN,
|
||||
marginRight: 8,
|
||||
paddingHorizontal: 7,
|
||||
},
|
||||
extraContent: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
color: theme.centerChannelColor,
|
||||
},
|
||||
text: {
|
||||
marginLeft: 8,
|
||||
color: theme.centerChannelColor,
|
||||
...typography('Body', 100, 'SemiBold'),
|
||||
},
|
||||
remove: {
|
||||
justifyContent: 'center',
|
||||
marginLeft: 7,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export default function SelectedChip({
|
||||
id,
|
||||
text,
|
||||
extra,
|
||||
onRemove,
|
||||
testID,
|
||||
}: SelectedChipProps) {
|
||||
const theme = useTheme();
|
||||
const style = getStyleFromTheme(theme);
|
||||
|
||||
const onPress = useCallback(() => {
|
||||
onRemove(id);
|
||||
}, [onRemove, id]);
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
entering={FadeIn.duration(FADE_DURATION)}
|
||||
exiting={FadeOut.duration(FADE_DURATION)}
|
||||
style={style.container}
|
||||
testID={testID}
|
||||
>
|
||||
{extra && (
|
||||
<View style={style.extraContent}>
|
||||
{extra}
|
||||
</View>
|
||||
)}
|
||||
<Text
|
||||
style={style.text}
|
||||
testID={`${testID}.display_name`}
|
||||
>
|
||||
{text}
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
style={style.remove}
|
||||
onPress={onPress}
|
||||
testID={`${testID}.remove.button`}
|
||||
>
|
||||
<CompassIcon
|
||||
name='close-circle'
|
||||
size={18}
|
||||
color={changeOpacity(theme.centerChannelColor, 0.32)}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import {LayoutChangeEvent, Platform, ScrollView, useWindowDimensions, View} from
|
||||
import Animated, {useAnimatedStyle, useDerivedValue, useSharedValue, withTiming} from 'react-native-reanimated';
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context';
|
||||
|
||||
import {USER_CHIP_BOTTOM_MARGIN, USER_CHIP_HEIGHT} from '@components/selected_chip';
|
||||
import Toast from '@components/toast';
|
||||
import {General} from '@constants';
|
||||
import {useTheme} from '@context/theme';
|
||||
@@ -14,7 +13,7 @@ import {useIsTablet, useKeyboardHeightWithDuration} from '@hooks/device';
|
||||
import Button from '@screens/bottom_sheet/button';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import SelectedUser from './selected_user';
|
||||
import SelectedUser, {USER_CHIP_BOTTOM_MARGIN, USER_CHIP_HEIGHT} from './selected_user';
|
||||
|
||||
type Props = {
|
||||
|
||||
|
||||
@@ -3,9 +3,18 @@
|
||||
|
||||
import React, {useCallback} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import Animated, {FadeIn, FadeOut} from 'react-native-reanimated';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import ProfilePicture from '@components/profile_picture';
|
||||
import SelectedChip from '@components/selected_chip';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
import {displayUsername} from '@utils/user';
|
||||
|
||||
type Props = {
|
||||
@@ -31,34 +40,87 @@ type Props = {
|
||||
testID?: string;
|
||||
}
|
||||
|
||||
export const USER_CHIP_HEIGHT = 32;
|
||||
export const USER_CHIP_BOTTOM_MARGIN = 8;
|
||||
const FADE_DURATION = 100;
|
||||
|
||||
const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'row',
|
||||
borderRadius: 16,
|
||||
height: USER_CHIP_HEIGHT,
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.08),
|
||||
marginBottom: USER_CHIP_BOTTOM_MARGIN,
|
||||
marginRight: 8,
|
||||
paddingHorizontal: 7,
|
||||
},
|
||||
remove: {
|
||||
justifyContent: 'center',
|
||||
marginLeft: 7,
|
||||
},
|
||||
profileContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginRight: 8,
|
||||
color: theme.centerChannelColor,
|
||||
},
|
||||
text: {
|
||||
color: theme.centerChannelColor,
|
||||
...typography('Body', 100, 'SemiBold'),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export default function SelectedUser({
|
||||
teammateNameDisplay,
|
||||
user,
|
||||
onRemove,
|
||||
testID,
|
||||
}: Props) {
|
||||
const theme = useTheme();
|
||||
const style = getStyleFromTheme(theme);
|
||||
const intl = useIntl();
|
||||
|
||||
const onPress = useCallback((id: string) => {
|
||||
onRemove(id);
|
||||
}, [onRemove]);
|
||||
const onPress = useCallback(() => {
|
||||
onRemove(user.id);
|
||||
}, [onRemove, user.id]);
|
||||
|
||||
const userItemTestID = `${testID}.${user.id}`;
|
||||
|
||||
return (
|
||||
<SelectedChip
|
||||
id={user.id}
|
||||
text={displayUsername(user, intl.locale, teammateNameDisplay)}
|
||||
extra={(
|
||||
<Animated.View
|
||||
entering={FadeIn.duration(FADE_DURATION)}
|
||||
exiting={FadeOut.duration(FADE_DURATION)}
|
||||
style={style.container}
|
||||
testID={`${testID}.${user.id}`}
|
||||
>
|
||||
<View style={style.profileContainer}>
|
||||
<ProfilePicture
|
||||
author={user}
|
||||
size={20}
|
||||
iconSize={20}
|
||||
testID={`${userItemTestID}.profile_picture`}
|
||||
/>
|
||||
)}
|
||||
onRemove={onPress}
|
||||
testID={userItemTestID}
|
||||
/>
|
||||
</View>
|
||||
<Text
|
||||
style={style.text}
|
||||
testID={`${testID}.${user.id}.display_name`}
|
||||
>
|
||||
{displayUsername(user, intl.locale, teammateNameDisplay)}
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
style={style.remove}
|
||||
onPress={onPress}
|
||||
testID={`${testID}.${user.id}.remove.button`}
|
||||
>
|
||||
<CompassIcon
|
||||
name='close-circle'
|
||||
size={18}
|
||||
color={changeOpacity(theme.centerChannelColor, 0.32)}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,8 +9,9 @@ import {map} from 'rxjs/operators';
|
||||
|
||||
import FormattedText from '@components/formatted_text';
|
||||
import FormattedTime from '@components/formatted_time';
|
||||
import {getDisplayNamePreferenceAsBool} from '@helpers/api/preference';
|
||||
import {queryDisplayNamePreferences} from '@queries/servers/preference';
|
||||
import {Preferences} from '@constants';
|
||||
import {getPreferenceAsBool} from '@helpers/api/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {observeConfigBooleanValue} from '@queries/servers/system';
|
||||
import {observeCurrentUser} from '@queries/servers/user';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
@@ -79,11 +80,11 @@ const SystemHeader = ({isMilitaryTime, isTimezoneEnabled, createAt, theme, user}
|
||||
};
|
||||
|
||||
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
const preferences = queryDisplayNamePreferences(database, 'use_military_time').
|
||||
const preferences = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time').
|
||||
observeWithColumns(['value']);
|
||||
const isTimezoneEnabled = observeConfigBooleanValue(database, 'ExperimentalTimezone');
|
||||
const isMilitaryTime = preferences.pipe(
|
||||
map((prefs) => getDisplayNamePreferenceAsBool(prefs, 'use_military_time')),
|
||||
map((prefs) => getPreferenceAsBool(prefs, Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', false)),
|
||||
);
|
||||
const user = observeCurrentUser(database);
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ const withTeams = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
const teamIds = queryJoinedTeams(database).observe().pipe(
|
||||
map((ts) => ts.map((t) => ({id: t.id, displayName: t.displayName}))),
|
||||
);
|
||||
const order = queryPreferencesByCategoryAndName(database, Preferences.CATEGORIES.TEAMS_ORDER).
|
||||
const order = queryPreferencesByCategoryAndName(database, Preferences.TEAMS_ORDER).
|
||||
observeWithColumns(['value']).pipe(
|
||||
switchMap((p) => (p.length ? of$(p[0].value.split(',')) : of$([]))),
|
||||
);
|
||||
|
||||
@@ -7,8 +7,8 @@ import {of as of$} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import Preferences from '@constants/preferences';
|
||||
import {getSidebarPreferenceAsBool} from '@helpers/api/preference';
|
||||
import {querySidebarPreferences} from '@queries/servers/preference';
|
||||
import {getPreferenceAsBool} from '@helpers/api/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {observeCurrentChannelId, observeCurrentTeamId, observeOnlyUnreads} from '@queries/servers/system';
|
||||
import {observeUnreadsAndMentionsInTeam} from '@queries/servers/thread';
|
||||
|
||||
@@ -22,10 +22,10 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
|
||||
return {
|
||||
currentChannelId: observeCurrentChannelId(database),
|
||||
groupUnreadsSeparately: querySidebarPreferences(database, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS).
|
||||
groupUnreadsSeparately: queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS).
|
||||
observeWithColumns(['value']).
|
||||
pipe(
|
||||
switchMap((prefs: PreferenceModel[]) => of$(getSidebarPreferenceAsBool(prefs, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS))),
|
||||
switchMap((prefs: PreferenceModel[]) => of$(getPreferenceAsBool(prefs, Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS, false))),
|
||||
),
|
||||
onlyUnreads: observeOnlyUnreads(database),
|
||||
unreadsAndMentions: currentTeamId.pipe(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useMemo} from 'react';
|
||||
import React from 'react';
|
||||
import {IntlShape, useIntl} from 'react-intl';
|
||||
import {StyleProp, Text, View, ViewStyle} from 'react-native';
|
||||
|
||||
@@ -81,6 +81,7 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme: Theme) => {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.64),
|
||||
fontSize: 15,
|
||||
fontFamily: 'OpenSans',
|
||||
flexShrink: 5,
|
||||
},
|
||||
icon: {
|
||||
marginLeft: 4,
|
||||
@@ -112,19 +113,6 @@ const UserItem = ({
|
||||
|
||||
const userItemTestId = `${testID}.${user?.id}`;
|
||||
|
||||
let rowUsernameFlexShrink = 1;
|
||||
if (user) {
|
||||
for (const rowInfoElem of [bot, guest, Boolean(name.length), isCurrentUser]) {
|
||||
if (rowInfoElem) {
|
||||
rowUsernameFlexShrink++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const usernameTextStyle = useMemo(() => {
|
||||
return [style.rowUsername, {flexShrink: rowUsernameFlexShrink}];
|
||||
}, [user, rowUsernameFlexShrink]);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[style.row, containerStyle]}
|
||||
@@ -139,7 +127,7 @@ const UserItem = ({
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
style={[style.rowInfo, {maxWidth: shared ? '75%' : '85%'}]}
|
||||
style={[style.rowInfo, {maxWidth: shared ? '75%' : '80%'}]}
|
||||
>
|
||||
{bot && <BotTag testID={`${userItemTestId}.bot.tag`}/>}
|
||||
{guest && <GuestTag testID={`${userItemTestId}.guest.tag`}/>}
|
||||
@@ -160,15 +148,15 @@ const UserItem = ({
|
||||
testID={`${userItemTestId}.current_user_indicator`}
|
||||
/>
|
||||
}
|
||||
{Boolean(user) && (
|
||||
<Text
|
||||
style={usernameTextStyle}
|
||||
numberOfLines={1}
|
||||
testID={`${userItemTestId}.username`}
|
||||
>
|
||||
{` @${user!.username}`}
|
||||
</Text>
|
||||
)}
|
||||
{Boolean(user) &&
|
||||
<Text
|
||||
style={style.rowUsername}
|
||||
numberOfLines={1}
|
||||
testID={`${userItemTestId}.username`}
|
||||
>
|
||||
{` @${user!.username}`}
|
||||
</Text>
|
||||
}
|
||||
</View>
|
||||
{Boolean(isCustomStatusEnabled && !bot && customStatus?.emoji && !customStatusExpired) && (
|
||||
<CustomStatusEmoji
|
||||
|
||||
@@ -928,506 +928,3 @@ exports[`components/channel_list_row should show results no tutorial 1`] = `
|
||||
</View>
|
||||
</RCTScrollView>
|
||||
`;
|
||||
|
||||
exports[`components/channel_list_row should show results no tutorial 2 users 1`] = `
|
||||
<RCTScrollView
|
||||
ListEmptyComponent={[Function]}
|
||||
ListFooterComponent={[Function]}
|
||||
contentContainerStyle={
|
||||
{
|
||||
"flexGrow": 1,
|
||||
}
|
||||
}
|
||||
data={
|
||||
[
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"auth_service": "",
|
||||
"create_at": 1111,
|
||||
"delete_at": 0,
|
||||
"email": "john@doe.com",
|
||||
"first_name": "",
|
||||
"id": "1",
|
||||
"last_name": "",
|
||||
"locale": "",
|
||||
"nickname": "",
|
||||
"notify_props": {
|
||||
"channel": "true",
|
||||
"comments": "never",
|
||||
"desktop": "mention",
|
||||
"desktop_sound": "true",
|
||||
"email": "true",
|
||||
"first_name": "true",
|
||||
"mention_keys": "",
|
||||
"push": "mention",
|
||||
"push_status": "away",
|
||||
},
|
||||
"position": "",
|
||||
"roles": "",
|
||||
"update_at": 1111,
|
||||
"username": "johndoe",
|
||||
},
|
||||
],
|
||||
"first": true,
|
||||
"id": "J",
|
||||
},
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"auth_service": "",
|
||||
"create_at": 1111,
|
||||
"delete_at": 0,
|
||||
"email": "rocky@doe.com",
|
||||
"first_name": "",
|
||||
"id": "2",
|
||||
"last_name": "",
|
||||
"locale": "",
|
||||
"nickname": "",
|
||||
"notify_props": {
|
||||
"channel": "true",
|
||||
"comments": "never",
|
||||
"desktop": "mention",
|
||||
"desktop_sound": "true",
|
||||
"email": "true",
|
||||
"first_name": "true",
|
||||
"mention_keys": "",
|
||||
"push": "mention",
|
||||
"push_status": "away",
|
||||
},
|
||||
"position": "",
|
||||
"roles": "",
|
||||
"update_at": 1111,
|
||||
"username": "rocky",
|
||||
},
|
||||
],
|
||||
"first": false,
|
||||
"id": "R",
|
||||
},
|
||||
]
|
||||
}
|
||||
getItem={[Function]}
|
||||
getItemCount={[Function]}
|
||||
initialNumToRender={15}
|
||||
keyExtractor={[Function]}
|
||||
keyboardDismissMode="on-drag"
|
||||
keyboardShouldPersistTaps="always"
|
||||
maxToRenderPerBatch={16}
|
||||
onContentSizeChange={[Function]}
|
||||
onEndReached={[Function]}
|
||||
onLayout={[Function]}
|
||||
onMomentumScrollBegin={[Function]}
|
||||
onMomentumScrollEnd={[Function]}
|
||||
onScroll={[Function]}
|
||||
onScrollBeginDrag={[Function]}
|
||||
onScrollEndDrag={[Function]}
|
||||
removeClippedSubviews={true}
|
||||
renderItem={[Function]}
|
||||
scrollEventThrottle={60}
|
||||
stickyHeaderIndices={[]}
|
||||
style={
|
||||
{
|
||||
"backgroundColor": "#ffffff",
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
testID="UserListRow.section_list"
|
||||
>
|
||||
<View>
|
||||
<View
|
||||
onFocusCapture={[Function]}
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"backgroundColor": "#ffffff",
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"backgroundColor": "rgba(63,67,80,0.08)",
|
||||
"height": 24,
|
||||
"justifyContent": "center",
|
||||
"paddingLeft": 16,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
{
|
||||
"color": "#3f4350",
|
||||
"fontFamily": "OpenSans-SemiBold",
|
||||
"fontSize": 12,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 16,
|
||||
}
|
||||
}
|
||||
>
|
||||
J
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onFocusCapture={[Function]}
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
onMoveShouldSetResponder={[Function]}
|
||||
onMoveShouldSetResponderCapture={[Function]}
|
||||
onResponderEnd={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderReject={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderStart={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
onStartShouldSetResponderCapture={[Function]}
|
||||
>
|
||||
<View
|
||||
accessibilityValue={
|
||||
{
|
||||
"max": undefined,
|
||||
"min": undefined,
|
||||
"now": undefined,
|
||||
"text": undefined,
|
||||
}
|
||||
}
|
||||
accessible={true}
|
||||
focusable={true}
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
"height": 58,
|
||||
"overflow": "hidden",
|
||||
"paddingHorizontal": 20,
|
||||
}
|
||||
}
|
||||
testID="create_direct_message.user_list.user_item.1"
|
||||
>
|
||||
<View
|
||||
style={
|
||||
[
|
||||
{
|
||||
"alignItems": "center",
|
||||
"color": "#3f4350",
|
||||
"flexDirection": "row",
|
||||
},
|
||||
{
|
||||
"opacity": 1,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"borderRadius": 21.5,
|
||||
"height": 42,
|
||||
"width": 42,
|
||||
}
|
||||
}
|
||||
testID="create_direct_message.user_list.user_item.1.profile_picture"
|
||||
>
|
||||
<Icon
|
||||
name="account-outline"
|
||||
size={24}
|
||||
style={
|
||||
{
|
||||
"color": "rgba(63,67,80,0.48)",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
[
|
||||
{
|
||||
"flex": 1,
|
||||
"flexDirection": "column",
|
||||
"justifyContent": "center",
|
||||
"paddingHorizontal": 10,
|
||||
},
|
||||
{
|
||||
"opacity": 1,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"flexDirection": "row",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
ellipsizeMode="tail"
|
||||
numberOfLines={1}
|
||||
style={
|
||||
{
|
||||
"color": "#3f4350",
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"height": 24,
|
||||
"lineHeight": 24,
|
||||
"maxWidth": "80%",
|
||||
}
|
||||
}
|
||||
testID="create_direct_message.user_list.user_item.1.display_name"
|
||||
>
|
||||
johndoe
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"alignItems": "center",
|
||||
"justifyContent": "center",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
color="rgba(63,67,80,0.32)"
|
||||
name="circle-outline"
|
||||
size={28}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onFocusCapture={[Function]}
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
/>
|
||||
<View
|
||||
onFocusCapture={[Function]}
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"backgroundColor": "#ffffff",
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"backgroundColor": "rgba(63,67,80,0.08)",
|
||||
"height": 24,
|
||||
"justifyContent": "center",
|
||||
"paddingLeft": 16,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
{
|
||||
"color": "#3f4350",
|
||||
"fontFamily": "OpenSans-SemiBold",
|
||||
"fontSize": 12,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 16,
|
||||
}
|
||||
}
|
||||
>
|
||||
R
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onFocusCapture={[Function]}
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
onMoveShouldSetResponder={[Function]}
|
||||
onMoveShouldSetResponderCapture={[Function]}
|
||||
onResponderEnd={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderReject={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderStart={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
onStartShouldSetResponderCapture={[Function]}
|
||||
>
|
||||
<View
|
||||
accessibilityValue={
|
||||
{
|
||||
"max": undefined,
|
||||
"min": undefined,
|
||||
"now": undefined,
|
||||
"text": undefined,
|
||||
}
|
||||
}
|
||||
accessible={true}
|
||||
focusable={true}
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
"height": 58,
|
||||
"overflow": "hidden",
|
||||
"paddingHorizontal": 20,
|
||||
}
|
||||
}
|
||||
testID="create_direct_message.user_list.user_item.2"
|
||||
>
|
||||
<View
|
||||
style={
|
||||
[
|
||||
{
|
||||
"alignItems": "center",
|
||||
"color": "#3f4350",
|
||||
"flexDirection": "row",
|
||||
},
|
||||
{
|
||||
"opacity": 1,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"borderRadius": 21.5,
|
||||
"height": 42,
|
||||
"width": 42,
|
||||
}
|
||||
}
|
||||
testID="create_direct_message.user_list.user_item.2.profile_picture"
|
||||
>
|
||||
<Icon
|
||||
name="account-outline"
|
||||
size={24}
|
||||
style={
|
||||
{
|
||||
"color": "rgba(63,67,80,0.48)",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
[
|
||||
{
|
||||
"flex": 1,
|
||||
"flexDirection": "column",
|
||||
"justifyContent": "center",
|
||||
"paddingHorizontal": 10,
|
||||
},
|
||||
{
|
||||
"opacity": 1,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"flexDirection": "row",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
ellipsizeMode="tail"
|
||||
numberOfLines={1}
|
||||
style={
|
||||
{
|
||||
"color": "#3f4350",
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"height": 24,
|
||||
"lineHeight": 24,
|
||||
"maxWidth": "80%",
|
||||
}
|
||||
}
|
||||
testID="create_direct_message.user_list.user_item.2.display_name"
|
||||
>
|
||||
rocky
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"alignItems": "center",
|
||||
"justifyContent": "center",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
color="rgba(63,67,80,0.32)"
|
||||
name="circle-outline"
|
||||
size={28}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onFocusCapture={[Function]}
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
/>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"alignItems": "center",
|
||||
"flex": 1,
|
||||
"justifyContent": "center",
|
||||
}
|
||||
}
|
||||
>
|
||||
<ActivityIndicator
|
||||
color="#1c58d9"
|
||||
size="large"
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</RCTScrollView>
|
||||
`;
|
||||
|
||||
@@ -38,34 +38,6 @@ describe('components/channel_list_row', () => {
|
||||
push_status: 'away',
|
||||
},
|
||||
};
|
||||
|
||||
const user2: UserProfile = {
|
||||
id: '2',
|
||||
create_at: 1111,
|
||||
update_at: 1111,
|
||||
delete_at: 0,
|
||||
username: 'rocky',
|
||||
auth_service: '',
|
||||
email: 'rocky@doe.com',
|
||||
nickname: '',
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
position: '',
|
||||
roles: '',
|
||||
locale: '',
|
||||
notify_props: {
|
||||
channel: 'true',
|
||||
comments: 'never',
|
||||
desktop: 'mention',
|
||||
desktop_sound: 'true',
|
||||
email: 'true',
|
||||
first_name: 'true',
|
||||
mention_keys: '',
|
||||
push: 'mention',
|
||||
push_status: 'away',
|
||||
},
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
const server = await TestHelper.setupServerDatabase();
|
||||
database = server.database;
|
||||
@@ -119,30 +91,6 @@ describe('components/channel_list_row', () => {
|
||||
expect(wrapper.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should show results no tutorial 2 users', () => {
|
||||
const wrapper = renderWithEverything(
|
||||
<UserList
|
||||
profiles={[user, user2]}
|
||||
testID='UserListRow'
|
||||
currentUserId={'1'}
|
||||
teammateNameDisplay={'johndoe'}
|
||||
handleSelectProfile={() => {
|
||||
// noop
|
||||
}}
|
||||
fetchMore={() => {
|
||||
// noop
|
||||
}}
|
||||
loading={true}
|
||||
selectedIds={{}}
|
||||
showNoResults={true}
|
||||
tutorialWatched={true}
|
||||
/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
expect(wrapper.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should show results and tutorial', () => {
|
||||
const wrapper = renderWithEverything(
|
||||
<UserList
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback, useMemo} from 'react';
|
||||
import {defineMessages, IntlShape, useIntl} from 'react-intl';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {FlatList, Keyboard, ListRenderItemInfo, Platform, SectionList, SectionListData, Text, View} from 'react-native';
|
||||
|
||||
import {storeProfile} from '@actions/local/user';
|
||||
@@ -13,7 +13,6 @@ import {General, Screens} from '@constants';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {useKeyboardHeight} from '@hooks/device';
|
||||
import {t} from '@i18n';
|
||||
import {openAsBottomSheet} from '@screens/navigation';
|
||||
import {
|
||||
changeOpacity,
|
||||
@@ -21,23 +20,9 @@ import {
|
||||
} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
type UserProfileWithChannelAdmin = UserProfile & {scheme_admin?: boolean}
|
||||
type RenderItemType = ListRenderItemInfo<UserProfileWithChannelAdmin> & {section?: SectionListData<UserProfileWithChannelAdmin>}
|
||||
|
||||
const INITIAL_BATCH_TO_RENDER = 15;
|
||||
const SCROLL_EVENT_THROTTLE = 60;
|
||||
|
||||
const messages = defineMessages({
|
||||
admins: {
|
||||
id: t('mobile.manage_members.section_title_admins'),
|
||||
defaultMessage: 'CHANNEL ADMINS',
|
||||
},
|
||||
members: {
|
||||
id: t('mobile.manage_members.section_title_members'),
|
||||
defaultMessage: 'MEMBERS',
|
||||
},
|
||||
});
|
||||
|
||||
const keyboardDismissProp = Platform.select({
|
||||
android: {
|
||||
onScrollBeginDrag: Keyboard.dismiss,
|
||||
@@ -56,58 +41,29 @@ const sectionKeyExtractor = (profile: UserProfile) => {
|
||||
return profile.username[0].toUpperCase();
|
||||
};
|
||||
|
||||
const sectionRoleKeyExtractor = (cAdmin: boolean) => {
|
||||
// Group items by channel admin or channel member
|
||||
return cAdmin ? messages.admins : messages.members;
|
||||
};
|
||||
export function createProfilesSections(profiles: UserProfile[]) {
|
||||
const sections: {[key: string]: UserProfile[]} = {};
|
||||
const sectionKeys: string[] = [];
|
||||
for (const profile of profiles) {
|
||||
const sectionKey = sectionKeyExtractor(profile);
|
||||
|
||||
export function createProfilesSections(intl: IntlShape, profiles: UserProfile[], members?: ChannelMember[]) {
|
||||
if (!profiles.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const sections = new Map();
|
||||
|
||||
if (members?.length) {
|
||||
// when channel members are provided, build the sections by admins and members
|
||||
const membersDictionary = new Map();
|
||||
const membersSections = new Map();
|
||||
const {formatMessage} = intl;
|
||||
members.forEach((m) => membersDictionary.set(m.user_id, m));
|
||||
profiles.forEach((p) => {
|
||||
const member = membersDictionary.get(p.id);
|
||||
const sectionKey = sectionRoleKeyExtractor(member.scheme_admin!);
|
||||
const sectionValue = membersSections.get(sectionKey) || [];
|
||||
|
||||
// combine UserProfile and ChannelMember objects so can get channel member scheme_admin permission
|
||||
const section = [...sectionValue, {...p, ...member}];
|
||||
membersSections.set(sectionKey, section);
|
||||
});
|
||||
sections.set(formatMessage(messages.admins), membersSections.get(messages.admins));
|
||||
sections.set(formatMessage(messages.members), membersSections.get(messages.members));
|
||||
} else {
|
||||
// when channel members are not provided, build the sections alphabetically
|
||||
profiles.forEach((p) => {
|
||||
const sectionKey = sectionKeyExtractor(p);
|
||||
const sectionValue = sections.get(sectionKey) || [];
|
||||
const section = [...sectionValue, p];
|
||||
sections.set(sectionKey, section);
|
||||
});
|
||||
}
|
||||
|
||||
const results = [];
|
||||
let index = 0;
|
||||
for (const [k, v] of sections) {
|
||||
if (v) {
|
||||
results.push({
|
||||
first: index === 0,
|
||||
id: k,
|
||||
data: v,
|
||||
});
|
||||
if (!sections[sectionKey]) {
|
||||
sections[sectionKey] = [];
|
||||
sectionKeys.push(sectionKey);
|
||||
}
|
||||
index++;
|
||||
|
||||
sections[sectionKey].push(profile);
|
||||
}
|
||||
return results;
|
||||
|
||||
sectionKeys.sort();
|
||||
|
||||
return sectionKeys.map((sectionKey, index) => {
|
||||
return {
|
||||
id: sectionKey,
|
||||
first: index === 0,
|
||||
data: sections[sectionKey],
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
@@ -147,14 +103,11 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
|
||||
type Props = {
|
||||
profiles: UserProfile[];
|
||||
channelMembers?: ChannelMember[];
|
||||
currentUserId: string;
|
||||
teammateNameDisplay: string;
|
||||
handleSelectProfile: (user: UserProfile) => void;
|
||||
fetchMore: () => void;
|
||||
loading: boolean;
|
||||
manageMode?: boolean;
|
||||
showManageMode?: boolean;
|
||||
showNoResults: boolean;
|
||||
selectedIds: {[id: string]: UserProfile};
|
||||
testID?: string;
|
||||
@@ -164,15 +117,12 @@ type Props = {
|
||||
|
||||
export default function UserList({
|
||||
profiles,
|
||||
channelMembers,
|
||||
selectedIds,
|
||||
currentUserId,
|
||||
teammateNameDisplay,
|
||||
handleSelectProfile,
|
||||
fetchMore,
|
||||
loading,
|
||||
manageMode = false,
|
||||
showManageMode = false,
|
||||
showNoResults,
|
||||
term,
|
||||
testID,
|
||||
@@ -189,16 +139,11 @@ export default function UserList({
|
||||
], [style, keyboardHeight]);
|
||||
|
||||
const data = useMemo(() => {
|
||||
if (profiles.length === 0 && !loading) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (term) {
|
||||
return profiles;
|
||||
}
|
||||
|
||||
return createProfilesSections(intl, profiles, channelMembers);
|
||||
}, [channelMembers, loading, profiles, term]);
|
||||
return createProfilesSections(profiles);
|
||||
}, [term, profiles]);
|
||||
|
||||
const openUserProfile = useCallback(async (profile: UserProfile) => {
|
||||
const {user} = await storeProfile(serverUrl, profile);
|
||||
@@ -217,34 +162,29 @@ export default function UserList({
|
||||
}
|
||||
}, []);
|
||||
|
||||
const renderItem = useCallback(({item, index, section}: RenderItemType) => {
|
||||
const renderItem = useCallback(({item, index, section}: ListRenderItemInfo<UserProfile> & {section?: SectionListData<UserProfile>}) => {
|
||||
// The list will re-render when the selection changes because it's passed into the list as extraData
|
||||
const selected = Boolean(selectedIds[item.id]);
|
||||
const canAdd = Object.keys(selectedIds).length < General.MAX_USERS_IN_GM;
|
||||
|
||||
const isChAdmin = item.scheme_admin || false;
|
||||
|
||||
return (
|
||||
<UserListRow
|
||||
key={item.id}
|
||||
highlight={section?.first && index === 0}
|
||||
id={item.id}
|
||||
isChannelAdmin={isChAdmin}
|
||||
isMyUser={currentUserId === item.id}
|
||||
manageMode={manageMode}
|
||||
onPress={handleSelectProfile}
|
||||
onLongPress={openUserProfile}
|
||||
selectable={manageMode || canAdd}
|
||||
disabled={!canAdd}
|
||||
selectable={true}
|
||||
selected={selected}
|
||||
showManageMode={showManageMode}
|
||||
testID='create_direct_message.user_list.user_item'
|
||||
teammateNameDisplay={teammateNameDisplay}
|
||||
tutorialWatched={tutorialWatched}
|
||||
user={item}
|
||||
/>
|
||||
);
|
||||
}, [selectedIds, handleSelectProfile, showManageMode, manageMode, teammateNameDisplay, tutorialWatched]);
|
||||
}, [selectedIds, currentUserId, handleSelectProfile, teammateNameDisplay, tutorialWatched]);
|
||||
|
||||
const renderLoading = useCallback(() => {
|
||||
if (!loading) {
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
|
||||
import {storeProfileLongPressTutorial} from '@actions/app/global';
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import FormattedText from '@components/formatted_text';
|
||||
import ProfilePicture from '@components/profile_picture';
|
||||
import {BotTag, GuestTag} from '@components/tag';
|
||||
import TouchableWithFeedback from '@components/touchable_with_feedback';
|
||||
@@ -20,27 +19,23 @@ import TutorialHighlight from '@components/tutorial_highlight';
|
||||
import TutorialLongPress from '@components/tutorial_highlight/long_press';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {useIsTablet} from '@hooks/device';
|
||||
import {t} from '@i18n';
|
||||
import {makeStyleSheetFromTheme, changeOpacity} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
import {displayUsername, isGuest} from '@utils/user';
|
||||
|
||||
type Props = {
|
||||
highlight?: boolean;
|
||||
id: string;
|
||||
isMyUser: boolean;
|
||||
isChannelAdmin: boolean;
|
||||
manageMode: boolean;
|
||||
onLongPress: (user: UserProfile) => void;
|
||||
highlight?: boolean;
|
||||
user: UserProfile;
|
||||
teammateNameDisplay: string;
|
||||
testID: string;
|
||||
onPress?: (user: UserProfile) => void;
|
||||
onLongPress: (user: UserProfile) => void;
|
||||
selectable: boolean;
|
||||
disabled?: boolean;
|
||||
selected: boolean;
|
||||
showManageMode: boolean;
|
||||
teammateNameDisplay: string;
|
||||
testID: string;
|
||||
tutorialWatched?: boolean;
|
||||
user: UserProfile;
|
||||
}
|
||||
|
||||
const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
@@ -89,15 +84,6 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
selectorManage: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
manageText: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.64),
|
||||
...typography('Body', 100, 'Regular'),
|
||||
},
|
||||
tutorial: {
|
||||
top: Platform.select({ios: -74, default: -94}),
|
||||
},
|
||||
@@ -114,26 +100,24 @@ function UserListRow({
|
||||
id,
|
||||
isMyUser,
|
||||
highlight,
|
||||
isChannelAdmin,
|
||||
user,
|
||||
teammateNameDisplay,
|
||||
testID,
|
||||
onPress,
|
||||
onLongPress,
|
||||
manageMode = false,
|
||||
tutorialWatched = false,
|
||||
selectable,
|
||||
disabled,
|
||||
selected,
|
||||
showManageMode = false,
|
||||
teammateNameDisplay,
|
||||
testID,
|
||||
tutorialWatched = false,
|
||||
user,
|
||||
}: Props) {
|
||||
const theme = useTheme();
|
||||
const intl = useIntl();
|
||||
const isTablet = useIsTablet();
|
||||
const [showTutorial, setShowTutorial] = useState(false);
|
||||
const [itemBounds, setItemBounds] = useState<TutorialItemBounds>({startX: 0, startY: 0, endX: 0, endY: 0});
|
||||
const viewRef = useRef<View>(null);
|
||||
const style = getStyleFromTheme(theme);
|
||||
const {formatMessage, locale} = useIntl();
|
||||
const {formatMessage} = intl;
|
||||
const {username} = user;
|
||||
|
||||
const startTutorial = () => {
|
||||
@@ -168,41 +152,13 @@ function UserListRow({
|
||||
}, [highlight, tutorialWatched, isTablet]);
|
||||
|
||||
const handlePress = useCallback(() => {
|
||||
if (isMyUser && manageMode) {
|
||||
return;
|
||||
}
|
||||
onPress?.(user);
|
||||
}, [onPress, isMyUser, manageMode, user]);
|
||||
}, [onPress, user]);
|
||||
|
||||
const handleLongPress = useCallback(() => {
|
||||
onLongPress?.(user);
|
||||
}, [onLongPress, user]);
|
||||
|
||||
const manageModeIcon = useMemo(() => {
|
||||
if (!showManageMode || isMyUser) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const color = changeOpacity(theme.centerChannelColor, 0.64);
|
||||
const i18nId = isChannelAdmin ? t('mobile.manage_members.admin') : t('mobile.manage_members.member');
|
||||
const defaultMessage = isChannelAdmin ? 'Admin' : 'Member';
|
||||
|
||||
return (
|
||||
<View style={style.selectorManage}>
|
||||
<FormattedText
|
||||
id={i18nId}
|
||||
style={style.manageText}
|
||||
defaultMessage={defaultMessage}
|
||||
/>
|
||||
<CompassIcon
|
||||
name={'chevron-down'}
|
||||
size={18}
|
||||
color={color}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}, [isChannelAdmin, showManageMode, theme]);
|
||||
|
||||
const onLayout = useCallback(() => {
|
||||
startTutorial();
|
||||
}, []);
|
||||
@@ -233,7 +189,7 @@ function UserListRow({
|
||||
}, {username});
|
||||
}
|
||||
|
||||
const teammateDisplay = displayUsername(user, locale, teammateNameDisplay);
|
||||
const teammateDisplay = displayUsername(user, intl.locale, teammateNameDisplay);
|
||||
const showTeammateDisplay = teammateDisplay !== username;
|
||||
|
||||
const userItemTestID = `${testID}.${id}`;
|
||||
@@ -301,7 +257,7 @@ function UserListRow({
|
||||
</View>
|
||||
}
|
||||
</View>
|
||||
{manageMode ? manageModeIcon : icon}
|
||||
{icon}
|
||||
</View>
|
||||
</TouchableWithFeedback>
|
||||
{showTutorial &&
|
||||
@@ -311,7 +267,7 @@ function UserListRow({
|
||||
onLayout={onLayout}
|
||||
>
|
||||
<TutorialLongPress
|
||||
message={formatMessage({id: 'user.tutorial.long_press', defaultMessage: "Long-press on an item to view a user's profile"})}
|
||||
message={intl.formatMessage({id: 'user.tutorial.long_press', defaultMessage: "Long-press on an item to view a user's profile"})}
|
||||
style={isTablet ? style.tutorialTablet : style.tutorial}
|
||||
/>
|
||||
</TutorialHighlight>
|
||||
|
||||
@@ -52,14 +52,13 @@ export const MIGRATION_EVENTS = keyMirror({
|
||||
});
|
||||
|
||||
export const SYSTEM_IDENTIFIERS = {
|
||||
CONFIG: 'config',
|
||||
CURRENT_CHANNEL_ID: 'currentChannelId',
|
||||
LAST_UNREAD_CHANNEL_ID: 'lastUnreadChannelId',
|
||||
CURRENT_TEAM_ID: 'currentTeamId',
|
||||
CURRENT_USER_ID: 'currentUserId',
|
||||
DATA_RETENTION_POLICIES: 'dataRetentionPolicies',
|
||||
EXPANDED_LINKS: 'expandedLinks',
|
||||
GRANULAR_DATA_RETENTION_POLICIES: 'granularDataRetentionPolicies',
|
||||
LAST_DATA_RETENTION_RUN: 'lastDataRetentionRun',
|
||||
GLOBAL_THREADS_TAB: 'globalThreadsTab',
|
||||
LAST_DISMISSED_BANNER: 'lastDismissedBanner',
|
||||
LAST_SERVER_VERSION_CHECK: 'LastServerVersionCheck',
|
||||
|
||||
@@ -15,8 +15,6 @@ export default keyMirror({
|
||||
LEAVE_TEAM: null,
|
||||
LOADING_CHANNEL_POSTS: null,
|
||||
NOTIFICATION_ERROR: null,
|
||||
REMOVE_USER_FROM_CHANNEL: null,
|
||||
MANAGE_USER_CHANGE_ROLE: null,
|
||||
SERVER_LOGOUT: null,
|
||||
SERVER_VERSION_CHANGED: null,
|
||||
SESSION_EXPIRED: null,
|
||||
|
||||
@@ -19,7 +19,6 @@ import Integrations from './integrations';
|
||||
import Launch from './launch';
|
||||
import License from './license';
|
||||
import List from './list';
|
||||
import Members from './members';
|
||||
import Navigation from './navigation';
|
||||
import Network from './network';
|
||||
import NotificationLevel from './notification_level';
|
||||
@@ -58,7 +57,6 @@ export {
|
||||
Launch,
|
||||
License,
|
||||
List,
|
||||
Members,
|
||||
Navigation,
|
||||
Network,
|
||||
NotificationLevel,
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import keyMirror from '@utils/key_mirror';
|
||||
const ManageOptions = keyMirror({
|
||||
REMOVE_USER: null,
|
||||
MAKE_CHANNEL_ADMIN: null,
|
||||
MAKE_CHANNEL_MEMBER: null,
|
||||
});
|
||||
|
||||
export type ManageOptionsTypes = keyof typeof ManageOptions
|
||||
|
||||
export default {
|
||||
ManageOptions,
|
||||
};
|
||||
|
||||
@@ -34,7 +34,6 @@ export const PostTypes: Record<string, string> = {
|
||||
|
||||
SYSTEM_AUTO_RESPONDER: 'system_auto_responder',
|
||||
CUSTOM_CALLS: 'custom_calls',
|
||||
CUSTOM_CALLS_RECORDING: 'custom_calls_recording',
|
||||
};
|
||||
|
||||
export const PostPriorityColors = {
|
||||
|
||||
@@ -1,26 +1,16 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export const CATEGORIES_TO_KEEP: Record<string, string> = {
|
||||
ADVANCED_SETTINGS: 'advanced_settings',
|
||||
DIRECT_CHANNEL_SHOW: 'direct_channel_show',
|
||||
GROUP_CHANNEL_SHOW: 'group_channel_show',
|
||||
DISPLAY_SETTINGS: 'display_settings',
|
||||
EMOJI: 'emoji',
|
||||
NOTIFICATIONS: 'notifications',
|
||||
SAVED_POST: 'flagged_post',
|
||||
SIDEBAR_SETTINGS: 'sidebar_settings',
|
||||
TEAMS_ORDER: 'teams_order',
|
||||
THEME: 'theme',
|
||||
};
|
||||
|
||||
const CATEGORIES: Record<string, string> = {
|
||||
...CATEGORIES_TO_KEEP,
|
||||
FAVORITE_CHANNEL: 'favorite_channel',
|
||||
};
|
||||
|
||||
const Preferences = {
|
||||
CATEGORIES,
|
||||
const Preferences: Record<string, any> = {
|
||||
CATEGORY_CHANNEL_OPEN_TIME: 'channel_open_time',
|
||||
CATEGORY_CHANNEL_APPROXIMATE_VIEW_TIME: 'channel_approximate_view_time',
|
||||
CATEGORY_DIRECT_CHANNEL_SHOW: 'direct_channel_show',
|
||||
CATEGORY_GROUP_CHANNEL_SHOW: 'group_channel_show',
|
||||
CATEGORY_EMOJI: 'emoji',
|
||||
CATEGORY_SAVED_POST: 'flagged_post',
|
||||
CATEGORY_FAVORITE_CHANNEL: 'favorite_channel',
|
||||
CATEGORY_AUTO_RESET_MANUAL_STATUS: 'auto_reset_manual_status',
|
||||
CATEGORY_NOTIFICATIONS: 'notifications',
|
||||
COLLAPSED_REPLY_THREADS: 'collapsed_reply_threads',
|
||||
COLLAPSED_REPLY_THREADS_OFF: 'off',
|
||||
COLLAPSED_REPLY_THREADS_ON: 'on',
|
||||
@@ -37,6 +27,7 @@ const Preferences = {
|
||||
// "immediate" is a 30 second interval
|
||||
INTERVAL_NEVER: 0,
|
||||
INTERVAL_NOT_SET: -1,
|
||||
CATEGORY_DISPLAY_SETTINGS: 'display_settings',
|
||||
NAME_NAME_FORMAT: 'name_format',
|
||||
DISPLAY_PREFER_NICKNAME: 'nickname_full_name',
|
||||
DISPLAY_PREFER_FULL_NAME: 'full_name',
|
||||
@@ -45,14 +36,18 @@ const Preferences = {
|
||||
LINK_PREVIEW_DISPLAY: 'link_previews',
|
||||
MENTION_KEYS: 'mention_keys',
|
||||
USE_MILITARY_TIME: 'use_military_time',
|
||||
CATEGORY_SIDEBAR_SETTINGS: 'sidebar_settings',
|
||||
CHANNEL_SIDEBAR_ORGANIZATION: 'channel_sidebar_organization',
|
||||
CHANNEL_SIDEBAR_LIMIT_DMS: 'limit_visible_dms_gms',
|
||||
CHANNEL_SIDEBAR_LIMIT_DMS_DEFAULT: 20,
|
||||
CHANNEL_SIDEBAR_GROUP_UNREADS: 'show_unread_section',
|
||||
AUTOCLOSE_DMS_ENABLED: 'after_seven_days',
|
||||
CATEGORY_ADVANCED_SETTINGS: 'advanced_settings',
|
||||
ADVANCED_FILTER_JOIN_LEAVE: 'join_leave',
|
||||
ADVANCED_CODE_BLOCK_ON_CTRL_ENTER: 'code_block_ctrl_enter',
|
||||
ADVANCED_SEND_ON_CTRL_ENTER: 'send_on_ctrl_enter',
|
||||
CATEGORY_THEME: 'theme',
|
||||
TEAMS_ORDER: 'teams_order',
|
||||
THEMES: {
|
||||
denim: {
|
||||
type: 'Denim',
|
||||
|
||||
@@ -28,12 +28,10 @@ export const GLOBAL_THREADS = 'GlobalThreads';
|
||||
export const HOME = 'Home';
|
||||
export const INTEGRATION_SELECTOR = 'IntegrationSelector';
|
||||
export const INTERACTIVE_DIALOG = 'InteractiveDialog';
|
||||
export const INVITE = 'Invite';
|
||||
export const IN_APP_NOTIFICATION = 'InAppNotification';
|
||||
export const JOIN_TEAM = 'JoinTeam';
|
||||
export const LATEX = 'Latex';
|
||||
export const LOGIN = 'Login';
|
||||
export const MANAGE_CHANNEL_MEMBERS = 'ManageChannelMembers';
|
||||
export const MENTIONS = 'Mentions';
|
||||
export const MFA = 'MFA';
|
||||
export const ONBOARDING = 'Onboarding';
|
||||
@@ -50,7 +48,6 @@ export const SETTINGS = 'Settings';
|
||||
export const SETTINGS_ADVANCED = 'SettingsAdvanced';
|
||||
export const SETTINGS_DISPLAY = 'SettingsDisplay';
|
||||
export const SETTINGS_DISPLAY_CLOCK = 'SettingsDisplayClock';
|
||||
export const SETTINGS_DISPLAY_CRT = 'SettingsDisplayCRT';
|
||||
export const SETTINGS_DISPLAY_THEME = 'SettingsDisplayTheme';
|
||||
export const SETTINGS_DISPLAY_TIMEZONE = 'SettingsDisplayTimezone';
|
||||
export const SETTINGS_DISPLAY_TIMEZONE_SELECT = 'SettingsDisplayTimezoneSelect';
|
||||
@@ -97,12 +94,10 @@ export default {
|
||||
HOME,
|
||||
INTEGRATION_SELECTOR,
|
||||
INTERACTIVE_DIALOG,
|
||||
INVITE,
|
||||
IN_APP_NOTIFICATION,
|
||||
JOIN_TEAM,
|
||||
LATEX,
|
||||
LOGIN,
|
||||
MANAGE_CHANNEL_MEMBERS,
|
||||
MENTIONS,
|
||||
MFA,
|
||||
ONBOARDING,
|
||||
@@ -119,7 +114,6 @@ export default {
|
||||
SETTINGS_ADVANCED,
|
||||
SETTINGS_DISPLAY,
|
||||
SETTINGS_DISPLAY_CLOCK,
|
||||
SETTINGS_DISPLAY_CRT,
|
||||
SETTINGS_DISPLAY_THEME,
|
||||
SETTINGS_DISPLAY_TIMEZONE,
|
||||
SETTINGS_DISPLAY_TIMEZONE_SELECT,
|
||||
@@ -150,8 +144,6 @@ export const MODAL_SCREENS_WITHOUT_BACK = new Set<string>([
|
||||
EDIT_SERVER,
|
||||
FIND_CHANNELS,
|
||||
GALLERY,
|
||||
MANAGE_CHANNEL_MEMBERS,
|
||||
INVITE,
|
||||
PERMALINK,
|
||||
]);
|
||||
|
||||
|
||||
@@ -5,6 +5,5 @@ export default {
|
||||
DELETED_ROOT_POST_ERROR: 'api.post.create_post.root_id.app_error',
|
||||
TOWN_SQUARE_READ_ONLY_ERROR: 'api.post.create_post.town_square_read_only',
|
||||
PLUGIN_DISMISSED_POST_ERROR: 'plugin.message_will_be_posted.dismiss_post',
|
||||
SEND_EMAIL_WITH_DEFAULTS_ERROR: 'api.team.invite_members.unable_to_send_email_with_defaults.app_error',
|
||||
TEAM_MEMBERSHIP_DENIAL_ERROR_ID: 'api.team.add_members.user_denied',
|
||||
};
|
||||
|
||||
@@ -9,7 +9,6 @@ export const SNACK_BAR_TYPE = keyMirror({
|
||||
LINK_COPIED: null,
|
||||
MESSAGE_COPIED: null,
|
||||
MUTE_CHANNEL: null,
|
||||
REMOVE_CHANNEL_USER: null,
|
||||
UNFAVORITE_CHANNEL: null,
|
||||
UNMUTE_CHANNEL: null,
|
||||
});
|
||||
@@ -46,12 +45,6 @@ export const SNACK_BAR_CONFIG: Record<string, SnackBarConfig> = {
|
||||
iconName: 'bell-off-outline',
|
||||
canUndo: true,
|
||||
},
|
||||
REMOVE_CHANNEL_USER: {
|
||||
id: t('snack.bar.remove.user'),
|
||||
defaultMessage: '1 member was removed from the channel',
|
||||
iconName: 'check',
|
||||
canUndo: true,
|
||||
},
|
||||
UNFAVORITE_CHANNEL: {
|
||||
id: t('snack.bar.unfavorite.channel'),
|
||||
defaultMessage: 'This channel was unfavorited',
|
||||
|
||||
@@ -6,7 +6,7 @@ import React, {ComponentType, createContext, useEffect, useState} from 'react';
|
||||
import {Appearance} from 'react-native';
|
||||
|
||||
import {Preferences} from '@constants';
|
||||
import {queryThemePreferences} from '@queries/servers/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {observeCurrentTeamId} from '@queries/servers/system';
|
||||
import {setThemeDefaults, updateThemeIfNeeded} from '@utils/theme';
|
||||
|
||||
@@ -97,7 +97,7 @@ export function useTheme(): Theme {
|
||||
|
||||
const enhancedThemeProvider = withObservables([], ({database}: {database: Database}) => ({
|
||||
currentTeamId: observeCurrentTeamId(database),
|
||||
themes: queryThemePreferences(database).observeWithColumns(['value']),
|
||||
themes: queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_THEME).observeWithColumns(['value']),
|
||||
}));
|
||||
|
||||
export default enhancedThemeProvider(ThemeProvider);
|
||||
|
||||
@@ -8,7 +8,7 @@ import {DeviceEventEmitter, Platform} from 'react-native';
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
import FileSystem from 'react-native-fs';
|
||||
|
||||
import {DatabaseType, MIGRATION_EVENTS, MM_TABLES} from '@constants/database';
|
||||
import {DatabaseType, MIGRATION_EVENTS, MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database';
|
||||
import AppDatabaseMigrations from '@database/migration/app';
|
||||
import ServerDatabaseMigrations from '@database/migration/server';
|
||||
import {InfoModel, GlobalModel, ServersModel} from '@database/models/app';
|
||||
@@ -24,10 +24,12 @@ import {schema as appSchema} from '@database/schema/app';
|
||||
import {serverSchema} from '@database/schema/server';
|
||||
import {beforeUpgrade} from '@helpers/database/upgrade';
|
||||
import {getActiveServer, getServer, getServerByIdentifier} from '@queries/app/servers';
|
||||
import {querySystemValue} from '@queries/servers/system';
|
||||
import {deleteLegacyFileCache} from '@utils/file';
|
||||
import {emptyFunction} from '@utils/general';
|
||||
import {logDebug, logError} from '@utils/log';
|
||||
import {deleteIOSDatabase, getIOSAppGroupDetails, renameIOSDatabase} from '@utils/mattermost_managed';
|
||||
import {urlSafeBase64Encode} from '@utils/security';
|
||||
import {hashCode_DEPRECATED, urlSafeBase64Encode} from '@utils/security';
|
||||
import {removeProtocol} from '@utils/url';
|
||||
|
||||
import type {AppDatabase, CreateServerDatabaseArgs, RegisterServerDatabaseArgs, Models, ServerDatabase, ServerDatabases} from '@typings/database/database';
|
||||
@@ -131,6 +133,12 @@ class DatabaseManager {
|
||||
if (serverUrl) {
|
||||
try {
|
||||
const databaseName = urlSafeBase64Encode(serverUrl);
|
||||
const oldDatabaseName = hashCode_DEPRECATED(serverUrl);
|
||||
|
||||
// Remove any legacy database we may already have.
|
||||
await this.renameDatabase(oldDatabaseName, databaseName);
|
||||
deleteLegacyFileCache(serverUrl);
|
||||
|
||||
const databaseFilePath = this.getDatabaseFilePath(databaseName);
|
||||
const migrations = ServerDatabaseMigrations;
|
||||
const modelClasses = this.serverModels;
|
||||
@@ -173,13 +181,38 @@ class DatabaseManager {
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private initServerDatabase = async (serverUrl: string): Promise<void> => {
|
||||
await this.createServerDatabase({
|
||||
const serverDatabase = await this.createServerDatabase({
|
||||
config: {
|
||||
dbName: serverUrl,
|
||||
dbType: DatabaseType.SERVER,
|
||||
serverUrl,
|
||||
},
|
||||
});
|
||||
|
||||
// Migration for config
|
||||
if (serverDatabase) {
|
||||
const {database, operator} = serverDatabase;
|
||||
const oldConfigList = await querySystemValue(database, SYSTEM_IDENTIFIERS.CONFIG).fetch();
|
||||
if (oldConfigList.length) {
|
||||
const oldConfigModel = oldConfigList[0];
|
||||
const oldConfig = oldConfigModel.value as ClientConfig;
|
||||
|
||||
const configs = [];
|
||||
let k: keyof ClientConfig;
|
||||
for (k in oldConfig) {
|
||||
// Check to silence eslint (guard-for-in)
|
||||
if (Object.prototype.hasOwnProperty.call(oldConfig, k)) {
|
||||
configs.push({
|
||||
id: k,
|
||||
value: oldConfig[k],
|
||||
});
|
||||
}
|
||||
}
|
||||
const models = await operator.handleConfigs({configs, configsToDelete: [], prepareRecordsOnly: true});
|
||||
|
||||
operator.batchRecords([...models, oldConfigModel.prepareDestroyPermanently()]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,6 +11,8 @@ import type GlobalModelInterface from '@typings/database/models/app/global';
|
||||
|
||||
const {GLOBAL} = MM_TABLES.APP;
|
||||
|
||||
// TODO : add TS definitions to sanitizer function signature.
|
||||
|
||||
/**
|
||||
* The Global model will act as a dictionary of name-value pairs. The value field can be a JSON object or any other
|
||||
* data type. It will hold information that applies to the whole app ( e.g. sidebar settings for tablets)
|
||||
|
||||
@@ -56,7 +56,7 @@ describe('** APP DATA OPERATOR **', () => {
|
||||
],
|
||||
tableName: 'Info',
|
||||
prepareRecordsOnly: false,
|
||||
}, 'handleInfo');
|
||||
});
|
||||
});
|
||||
|
||||
it('=> HandleGlobal: should write to GLOBAL table', async () => {
|
||||
@@ -79,6 +79,6 @@ describe('** APP DATA OPERATOR **', () => {
|
||||
createOrUpdateRawValues: globals,
|
||||
tableName: 'Global',
|
||||
prepareRecordsOnly: false,
|
||||
}, 'handleGlobal');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ export default class AppDataOperator extends BaseDataOperator {
|
||||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues: getUniqueRawsBy({raws: info, key: 'version_number'}),
|
||||
tableName: INFO,
|
||||
}, 'handleInfo');
|
||||
});
|
||||
};
|
||||
|
||||
handleGlobal = async ({globals, prepareRecordsOnly = true}: HandleGlobalArgs) => {
|
||||
@@ -45,6 +45,6 @@ export default class AppDataOperator extends BaseDataOperator {
|
||||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues: getUniqueRawsBy({raws: globals, key: 'id'}),
|
||||
tableName: GLOBAL,
|
||||
}, 'handleGlobal');
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,10 +22,10 @@ import type {
|
||||
|
||||
export interface BaseDataOperatorType {
|
||||
database: Database;
|
||||
handleRecords: <T extends Model>({buildKeyRecordBy, fieldName, transformer, createOrUpdateRawValues, deleteRawValues, tableName, prepareRecordsOnly}: HandleRecordsArgs<T>, description: string) => Promise<Model[]>;
|
||||
processRecords: <T extends Model>({createOrUpdateRawValues, deleteRawValues, tableName, buildKeyRecordBy, fieldName}: ProcessRecordsArgs) => Promise<ProcessRecordResults<T>>;
|
||||
batchRecords: (models: Model[], description: string) => Promise<void>;
|
||||
prepareRecords: <T extends Model>({tableName, createRaws, deleteRaws, updateRaws, transformer}: OperationArgs<T>) => Promise<Model[]>;
|
||||
handleRecords: ({buildKeyRecordBy, fieldName, transformer, createOrUpdateRawValues, deleteRawValues, tableName, prepareRecordsOnly}: HandleRecordsArgs) => Promise<Model[]>;
|
||||
processRecords: ({createOrUpdateRawValues, deleteRawValues, tableName, buildKeyRecordBy, fieldName}: ProcessRecordsArgs) => Promise<ProcessRecordResults>;
|
||||
batchRecords: (models: Model[]) => Promise<void>;
|
||||
prepareRecords: ({tableName, createRaws, deleteRaws, updateRaws, transformer}: OperationArgs) => Promise<Model[]>;
|
||||
}
|
||||
|
||||
export default class BaseDataOperator {
|
||||
@@ -45,7 +45,7 @@ export default class BaseDataOperator {
|
||||
* @param {(existing: Model, newElement: RawValue) => boolean} inputsArg.buildKeyRecordBy
|
||||
* @returns {Promise<{ProcessRecordResults}>}
|
||||
*/
|
||||
processRecords = async <T extends Model>({createOrUpdateRawValues = [], deleteRawValues = [], tableName, buildKeyRecordBy, fieldName}: ProcessRecordsArgs): Promise<ProcessRecordResults<T>> => {
|
||||
processRecords = async ({createOrUpdateRawValues = [], deleteRawValues = [], tableName, buildKeyRecordBy, fieldName}: ProcessRecordsArgs): Promise<ProcessRecordResults> => {
|
||||
const getRecords = async (rawValues: RawValue[]) => {
|
||||
// We will query a table where one of its fields can match a range of values. Hence, here we are extracting all those potential values.
|
||||
const columnValues: string[] = getRangeOfValues({fieldName, raws: rawValues});
|
||||
@@ -60,7 +60,7 @@ export default class BaseDataOperator {
|
||||
return [];
|
||||
}
|
||||
|
||||
const existingRecords = await retrieveRecords<T>({
|
||||
const existingRecords = await retrieveRecords({
|
||||
database: this.database,
|
||||
tableName,
|
||||
condition: Q.where(fieldName, Q.oneOf(columnValues)),
|
||||
@@ -125,13 +125,13 @@ export default class BaseDataOperator {
|
||||
* @param {(TransformerArgs) => Promise<Model>;} transformer
|
||||
* @returns {Promise<Model[]>}
|
||||
*/
|
||||
prepareRecords = async <T extends Model>({tableName, createRaws, deleteRaws, updateRaws, transformer}: OperationArgs<T>): Promise<T[]> => {
|
||||
prepareRecords = async ({tableName, createRaws, deleteRaws, updateRaws, transformer}: OperationArgs): Promise<Model[]> => {
|
||||
if (!this.database) {
|
||||
logWarning('Database not defined in prepareRecords');
|
||||
return [];
|
||||
}
|
||||
|
||||
let preparedRecords: Array<Promise<T>> = [];
|
||||
let preparedRecords: Array<Promise<Model>> = [];
|
||||
|
||||
// create operation
|
||||
if (createRaws?.length) {
|
||||
@@ -165,7 +165,7 @@ export default class BaseDataOperator {
|
||||
preparedRecords = preparedRecords.concat(recordPromises);
|
||||
}
|
||||
|
||||
const results = (await Promise.all(preparedRecords)) as T[];
|
||||
const results = await Promise.all(preparedRecords);
|
||||
|
||||
if (deleteRaws?.length) {
|
||||
deleteRaws.forEach((deleteRecord) => {
|
||||
@@ -182,12 +182,12 @@ export default class BaseDataOperator {
|
||||
* @param {Array} models
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async batchRecords(models: Model[], description: string): Promise<void> {
|
||||
async batchRecords(models: Model[]): Promise<void> {
|
||||
try {
|
||||
if (models.length > 0) {
|
||||
await this.database.write(async (writer) => {
|
||||
await writer.batch(models);
|
||||
}, description);
|
||||
await writer.batch(...models);
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
logWarning('batchRecords error ', e as Error);
|
||||
@@ -205,7 +205,7 @@ export default class BaseDataOperator {
|
||||
* @param {string} handleRecordsArgs.tableName
|
||||
* @returns {Promise<Model[]>}
|
||||
*/
|
||||
async handleRecords<T extends Model>({buildKeyRecordBy, fieldName, transformer, createOrUpdateRawValues, deleteRawValues = [], tableName, prepareRecordsOnly = true}: HandleRecordsArgs<T>, description: string): Promise<T[]> {
|
||||
async handleRecords({buildKeyRecordBy, fieldName, transformer, createOrUpdateRawValues, deleteRawValues = [], tableName, prepareRecordsOnly = true}: HandleRecordsArgs): Promise<Model[]> {
|
||||
if (!createOrUpdateRawValues.length) {
|
||||
logWarning(
|
||||
`An empty "rawValues" array has been passed to the handleRecords method for tableName ${tableName}`,
|
||||
@@ -213,7 +213,7 @@ export default class BaseDataOperator {
|
||||
return [];
|
||||
}
|
||||
|
||||
const {createRaws, deleteRaws, updateRaws} = await this.processRecords<T>({
|
||||
const {createRaws, deleteRaws, updateRaws} = await this.processRecords({
|
||||
createOrUpdateRawValues,
|
||||
deleteRawValues,
|
||||
tableName,
|
||||
@@ -221,8 +221,8 @@ export default class BaseDataOperator {
|
||||
fieldName,
|
||||
});
|
||||
|
||||
let models: T[] = [];
|
||||
models = await this.prepareRecords<T>({
|
||||
let models: Model[] = [];
|
||||
models = await this.prepareRecords({
|
||||
tableName,
|
||||
createRaws,
|
||||
updateRaws,
|
||||
@@ -231,7 +231,7 @@ export default class BaseDataOperator {
|
||||
});
|
||||
|
||||
if (!prepareRecordsOnly && models?.length) {
|
||||
await this.batchRecords(models, description);
|
||||
await this.batchRecords(models);
|
||||
}
|
||||
|
||||
return models;
|
||||
|
||||
@@ -46,7 +46,7 @@ describe('*** Operator: Category Handlers tests ***', () => {
|
||||
tableName: MM_TABLES.SERVER.CATEGORY,
|
||||
prepareRecordsOnly: false,
|
||||
transformer: transformCategoryRecord,
|
||||
}, 'handleCategories');
|
||||
});
|
||||
});
|
||||
|
||||
it('=> handleCategoryChannels: should write to the CATEGORY_CHANNEL table', async () => {
|
||||
@@ -74,6 +74,6 @@ describe('*** Operator: Category Handlers tests ***', () => {
|
||||
tableName: MM_TABLES.SERVER.CATEGORY_CHANNEL,
|
||||
prepareRecordsOnly: false,
|
||||
transformer: transformCategoryChannelRecord,
|
||||
}, 'handleCategoryChannels');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
import {getUniqueRawsBy} from '@database/operator/utils/general';
|
||||
import {logWarning} from '@utils/log';
|
||||
|
||||
import type ServerDataOperatorBase from '.';
|
||||
import type {
|
||||
HandleCategoryChannelArgs,
|
||||
HandleCategoryArgs,
|
||||
@@ -29,7 +28,7 @@ export interface CategoryHandlerMix {
|
||||
handleCategories: ({categories, prepareRecordsOnly}: HandleCategoryArgs) => Promise<CategoryModel[]>;
|
||||
}
|
||||
|
||||
const CategoryHandler = <TBase extends Constructor<ServerDataOperatorBase>>(superclass: TBase) => class extends superclass {
|
||||
const CategoryHandler = (superclass: any) => class extends superclass {
|
||||
/**
|
||||
* handleCategories: Handler responsible for the Create/Update operations occurring on the Category table from the 'Server' schema
|
||||
* @param {HandleCategoryArgs} categoriesArgs
|
||||
@@ -79,7 +78,7 @@ const CategoryHandler = <TBase extends Constructor<ServerDataOperatorBase>>(supe
|
||||
createOrUpdateRawValues,
|
||||
tableName: CATEGORY,
|
||||
prepareRecordsOnly,
|
||||
}, 'handleCategories');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -89,7 +88,7 @@ const CategoryHandler = <TBase extends Constructor<ServerDataOperatorBase>>(supe
|
||||
* @param {boolean} categoriesArgs.prepareRecordsOnly
|
||||
* @returns {Promise<CategoryChannelModel[]>}
|
||||
*/
|
||||
handleCategoryChannels = async ({categoryChannels, prepareRecordsOnly = true}: HandleCategoryChannelArgs): Promise<CategoryChannelModel[]> => {
|
||||
handleCategoryChannels = async ({categoryChannels, prepareRecordsOnly = true}: HandleCategoryChannelArgs): Promise<CategoryModel[]> => {
|
||||
if (!categoryChannels?.length) {
|
||||
logWarning(
|
||||
'An empty or undefined "categoryChannels" array has been passed to the handleCategories method',
|
||||
@@ -106,7 +105,7 @@ const CategoryHandler = <TBase extends Constructor<ServerDataOperatorBase>>(supe
|
||||
createOrUpdateRawValues,
|
||||
tableName: CATEGORY_CHANNEL,
|
||||
prepareRecordsOnly,
|
||||
}, 'handleCategoryChannels');
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user