Compare commits

...

19 Commits

Author SHA1 Message Date
Mattermost Build
70119fc026 Bump app build number to 452 (#6974) (#6975)
(cherry picked from commit d417b95643)

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
2023-01-13 21:16:22 +02:00
Mattermost Build
0d9c6e0ad3 Fix the caret position when using the search phrase modifier (#6972) (#6973)
(cherry picked from commit 49fc180982)

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
2023-01-13 21:15:11 +02:00
Mattermost Build
f7d8ed9e1f Bump app build number to 451 (#6967) (#6968)
(cherry picked from commit 9411dbd669)

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
2023-01-12 13:07:36 +02:00
Mattermost Build
b8cc13d7fa Fix (#6960) (#6966)
(cherry picked from commit c8ded6ef3c)

Co-authored-by: Anurag Shivarathri <anurag6713@gmail.com>
2023-01-12 13:00:51 +02:00
Mattermost Build
317568b4c8 Fix some issues found by Sentry (#6962) (#6965)
(cherry picked from commit bb351c7376)

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
2023-01-12 11:11:14 +02:00
Mattermost Build
55f919dd27 Add String description in log arguments (#6961) (#6964)
(cherry picked from commit 247d8371d9)

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
2023-01-12 11:08:12 +02:00
Mattermost Build
da1b3dc71d Add back wrongly removed Ephemeral.theme assignment (#6959) (#6963)
(cherry picked from commit cab863d62f)

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
2023-01-12 11:03:57 +02:00
Elias Nahum
57a9ff31bf fix schema test 2023-01-11 22:23:03 +02:00
Elias Nahum
4f86a87bdc fix ci 2023-01-11 22:14:01 +02:00
Mattermost Build
9ab21b2f62 Bump build number to 450 (#6950) (#6955)
* Fix upgrade path

* Introduce Upgrade helper

* Reset server database schema version to 1

* Enable release builds on the CI

* Bump build number to 450

(cherry picked from commit 4199b13843)

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
2023-01-11 21:45:47 +02:00
Mattermost Build
1934945d72 Fix code syntax highlight on full screen view (#6944) (#6954)
(cherry picked from commit 8edf128d59)

Co-authored-by: Daniel Espino García <larkox@gmail.com>
2023-01-11 21:45:14 +02:00
Mattermost Build
5162e6b6e7 Fix connection banner showing when not needed (#6948) (#6953)
* Fix connection banner showing when not needed

* Fix some issues and some refactoring

(cherry picked from commit 6082a6a790)

Co-authored-by: Daniel Espino García <larkox@gmail.com>
2023-01-11 21:44:31 +02:00
Guillermo Vayá
9139a26967 Merge pull request #6951 from mattermost/weblate-6947
Weblate 6947
2023-01-11 13:14:49 +01:00
Kaya Zeren
56fbb3d842 Translated using Weblate (Turkish)
Currently translated at 93.6% (879 of 939 strings)

Translation: mattermost-languages-shipped/mattermost-mobile-v2
Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-mobile-v2/tr/
2023-01-11 12:53:17 +01:00
정성근
56349f865f Translated using Weblate (Korean)
Currently translated at 59.2% (556 of 939 strings)

Translation: mattermost-languages-shipped/mattermost-mobile-v2
Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-mobile-v2/ko/
2023-01-11 12:53:01 +01:00
Elias Nahum
fdf593bcec Fix navigation theming (#6946) 2023-01-11 09:54:07 +02:00
Elias Nahum
8e2e016a6c check for analytics enabled 2023-01-09 11:14:15 +02:00
Elias Nahum
4d9bc1fbed Bump app build number to 449 (#6940) 2023-01-07 18:44:27 +02:00
Elias Nahum
7351c7ccac fix Sentry import 2023-01-07 18:39:26 +02:00
35 changed files with 508 additions and 382 deletions

View File

@@ -309,13 +309,13 @@ jobs:
- save:
filename: "*.apk"
# build-android-release:
# executor: android
# steps:
# - build-android
# - persist
# - save:
# filename: "*.apk"
build-android-release:
executor: android
steps:
- build-android
- persist
- save:
filename: "*.apk"
build-android-pr:
executor: android
@@ -326,27 +326,27 @@ jobs:
- save:
filename: "*.apk"
# build-android-unsigned:
# executor: android
# steps:
# - checkout:
# path: ~/mattermost-mobile
# - npm-dependencies
# - assets
# - fastlane-dependencies:
# for: android
# - gradle-dependencies
# - run:
# name: Jetify Android libraries
# command: ./node_modules/.bin/jetify
# - run:
# working_directory: fastlane
# name: Run fastlane to build unsigned android
# no_output_timeout: 30m
# command: bundle exec fastlane android unsigned
# - persist
# - save:
# filename: "*.apk"
build-android-unsigned:
executor: android
steps:
- checkout:
path: ~/mattermost-mobile
- npm-dependencies
- assets
- fastlane-dependencies:
for: android
- gradle-dependencies
- run:
name: Jetify Android libraries
command: ./node_modules/.bin/jetify
- run:
working_directory: fastlane
name: Run fastlane to build unsigned android
no_output_timeout: 30m
command: bundle exec fastlane android unsigned
- persist
- save:
filename: "*.apk"
build-ios-beta:
executor:
@@ -358,13 +358,13 @@ jobs:
- save:
filename: "*.ipa"
# build-ios-release:
# executor: ios
# steps:
# - build-ios
# - persist
# - save:
# filename: "*.ipa"
build-ios-release:
executor: ios
steps:
- build-ios
- persist
- save:
filename: "*.ipa"
build-ios-pr:
executor: ios
@@ -375,63 +375,64 @@ jobs:
- save:
filename: "*.ipa"
# build-ios-unsigned:
# executor: ios
# steps:
# - checkout:
# path: ~/mattermost-mobile
# - npm-dependencies
# - pods-dependencies
# - assets
# - fastlane-dependencies:
# for: ios
# - run:
# working_directory: fastlane
# name: Run fastlane to build unsigned iOS
# no_output_timeout: 30m
# command: |
# HOMEBREW_NO_AUTO_UPDATE=1 brew install watchman
# bundle exec fastlane ios unsigned
# - persist_to_workspace:
# root: ~/
# paths:
# - mattermost-mobile/*.ipa
# - save:
# filename: "*.ipa"
build-ios-unsigned:
executor: ios
steps:
- checkout:
path: ~/mattermost-mobile
- npm-dependencies
- pods-dependencies
- assets
- fastlane-dependencies:
for: ios
- run:
working_directory: fastlane
name: Run fastlane to build unsigned iOS
no_output_timeout: 30m
command: |
HOMEBREW_NO_AUTO_UPDATE=1 brew install watchman
bundle exec fastlane ios unsigned
- persist_to_workspace:
root: ~/
paths:
- mattermost-mobile/*.ipa
- save:
filename: "*.ipa"
# build-ios-simulator:
# executor: ios
# steps:
# - checkout:
# path: ~/mattermost-mobile
# - npm-dependencies
# - pods-dependencies
# - assets
# - fastlane-dependencies:
# for: ios
# - run:
# working_directory: fastlane
# name: Run fastlane to build unsigned x86_64 iOS app for iPhone simulator
# no_output_timeout: 30m
# command: |
# HOMEBREW_NO_AUTO_UPDATE=1 brew install watchman
# bundle exec fastlane ios simulator
# - persist_to_workspace:
# root: ~/
# paths:
# - mattermost-mobile/Mattermost-simulator-x86_64.app.zip
# - save:
# filename: "Mattermost-simulator-x86_64.app.zip"
build-ios-simulator:
executor: ios
steps:
- checkout:
path: ~/mattermost-mobile
- npm-dependencies
- pods-dependencies
- assets
- fastlane-dependencies:
for: ios
- run:
working_directory: fastlane
name: Run fastlane to build unsigned x86_64 iOS app for iPhone simulator
no_output_timeout: 30m
command: |
HOMEBREW_NO_AUTO_UPDATE=1 brew install watchman
bundle exec fastlane ios simulator
- persist_to_workspace:
root: ~/
paths:
- mattermost-mobile/Mattermost-simulator-x86_64.app.zip
- save:
filename: "Mattermost-simulator-x86_64.app.zip"
# deploy-android-release:
# executor:
# name: android
# resource_class: medium
# steps:
# - deploy-to-store:
# task: "Deploy to Google Play"
# target: android
# file: "*.apk"
deploy-android-release:
executor:
name: android
resource_class: medium
steps:
- deploy-to-store:
task: "Deploy to Google Play"
target: android
file: "*.apk"
env: "SUPPLY_TRACK=beta"
deploy-android-beta:
executor:
@@ -444,13 +445,14 @@ jobs:
file: "*.apk"
env: "SUPPLY_TRACK=alpha"
# deploy-ios-release:
# executor: ios
# steps:
# - deploy-to-store:
# task: "Deploy to TestFlight"
# target: ios
# file: "*.ipa"
deploy-ios-release:
executor: ios
steps:
- deploy-to-store:
task: "Deploy to TestFlight"
target: ios
file: "*.ipa"
env: ""
deploy-ios-beta:
executor: ios
@@ -461,17 +463,17 @@ jobs:
file: "*.ipa"
env: ""
# github-release:
# executor:
# name: android
# resource_class: medium
# steps:
# - attach_workspace:
# at: ~/
# - run:
# name: Create GitHub release
# working_directory: fastlane
# command: bundle exec fastlane github
github-release:
executor:
name: android
resource_class: medium
steps:
- attach_workspace:
at: ~/
- run:
name: Create GitHub release
working_directory: fastlane
command: bundle exec fastlane github
workflows:
version: 2
@@ -483,26 +485,24 @@ workflows:
# requires:
# - test
# - build-android-release:
# context: mattermost-mobile-android-release
# requires:
# - test
# filters:
# branches:
# only:
# - /^build-\d+$/
# - /^build-android-\d+$/
# - /^build-android-release-\d+$/
# - deploy-android-release:
# context: mattermost-mobile-android-release
# requires:
# - build-android-release
# filters:
# branches:
# only:
# - /^build-\d+$/
# - /^build-android-\d+$/
# - /^build-android-release-\d+$/
- build-android-release:
context: mattermost-mobile-android-release
requires:
- test
filters:
branches:
only:
- /^build-release-\d+$/
- /^build-android-release-\d+$/
- deploy-android-release:
context: mattermost-mobile-android-release
requires:
- build-android-release
filters:
branches:
only:
- /^build-release-\d+$/
- /^build-android-release-\d+$/
- build-android-beta:
context: mattermost-mobile-android-beta
@@ -523,26 +523,24 @@ workflows:
- /^build-android-\d+$/
- /^build-android-beta-\d+$/
# - build-ios-release:
# context: mattermost-mobile-ios-release
# requires:
# - test
# filters:
# branches:
# only:
# - /^build-\d+$/
# - /^build-ios-\d+$/
# - /^build-ios-release-\d+$/
# - deploy-ios-release:
# context: mattermost-mobile-ios-release
# requires:
# - build-ios-release
# filters:
# branches:
# only:
# - /^build-\d+$/
# - /^build-ios-\d+$/
# - /^build-ios-release-\d+$/
- build-ios-release:
context: mattermost-mobile-ios-release
requires:
- test
filters:
branches:
only:
- /^build-release-\d+$/
- /^build-ios-release-\d+$/
- deploy-ios-release:
context: mattermost-mobile-ios-release
requires:
- build-ios-release
filters:
branches:
only:
- /^build-release-\d+$/
- /^build-ios-release-\d+$/
- build-ios-beta:
context: mattermost-mobile-ios-beta
@@ -578,43 +576,41 @@ workflows:
branches:
only: /^(build|ios)-pr-.*/
# - build-android-unsigned:
# context: mattermost-mobile-unsigned
# requires:
# - test
# filters:
# tags:
# only: /^v(\d+\.)(\d+\.)(\d+)(.*)?$/
# branches:
# only: unsigned
# - build-ios-unsigned:
# context: mattermost-mobile-unsigned
# requires:
# - test
# filters:
# tags:
# only: /^v(\d+\.)(\d+\.)(\d+)(.*)?$/
# branches:
# only: unsigned
# - build-ios-simulator:
# context: mattermost-mobile-unsigned
# requires:
# - test
# filters:
# branches:
# only:
# - /^build-\d+$/
# - /^build-ios-\d+$/
# - /^build-ios-beta-\d+$/
# - /^build-ios-sim-\d+$/
- build-android-unsigned:
context: mattermost-mobile-unsigned
requires:
- test
filters:
tags:
only: /^v(\d+\.)(\d+\.)(\d+)(.*)?$/
branches:
only: unsigned
- build-ios-unsigned:
context: mattermost-mobile-unsigned
requires:
- test
filters:
tags:
only: /^v(\d+\.)(\d+\.)(\d+)(.*)?$/
branches:
only: unsigned
- build-ios-simulator:
context: mattermost-mobile-unsigned
requires:
- test
filters:
branches:
only:
- /^build-\d+$/
- /^build-ios-sim-\d+$/
# - github-release:
# context: mattermost-mobile-unsigned
# requires:
# - build-android-unsigned
# - build-ios-unsigned
# filters:
# tags:
# only: /^v(\d+\.)(\d+\.)(\d+)(.*)?$/
# branches:
# only: unsigned
- github-release:
context: mattermost-mobile-unsigned
requires:
- build-android-unsigned
- build-ios-unsigned
filters:
tags:
only: /^v(\d+\.)(\d+\.)(\d+)(.*)?$/
branches:
only: unsigned

View File

@@ -145,7 +145,7 @@ android {
applicationId "com.mattermost.rnbeta"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 448
versionCode 452
versionName "2.0.0"
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

View File

@@ -8,6 +8,7 @@ import {prepareCommonSystemValues, getCurrentTeamId, getWebSocketLastDisconnecte
import {getCurrentUser} from '@queries/servers/user';
import {setTeamLoading} from '@store/team_load_store';
import {deleteV1Data} from '@utils/file';
import {isTablet} from '@utils/helpers';
import {logInfo} from '@utils/log';
import {handleEntryAfterLoadNavigation, registerDeviceToken, syncOtherServers, verifyPushProxy} from './common';
@@ -29,7 +30,7 @@ export async function appEntry(serverUrl: string, since = 0, isUpgrade = false)
// clear lastUnreadChannelId
const removeLastUnreadChannelId = await prepareCommonSystemValues(operator, {lastUnreadChannelId: ''});
if (removeLastUnreadChannelId) {
operator.batchRecords(removeLastUnreadChannelId);
await operator.batchRecords(removeLastUnreadChannelId);
}
const {database} = operator;
@@ -47,7 +48,12 @@ export async function appEntry(serverUrl: string, since = 0, isUpgrade = false)
const {models, initialTeamId, initialChannelId, prefData, teamData, chData, meData} = entryData;
if (isUpgrade && meData?.user) {
const me = await prepareCommonSystemValues(operator, {currentUserId: meData.user.id});
const isTabletDevice = await isTablet();
const me = await prepareCommonSystemValues(operator, {
currentUserId: meData.user.id,
currentTeamId: initialTeamId,
currentChannelId: isTabletDevice ? initialChannelId : undefined,
});
if (me?.length) {
await operator.batchRecords(me);
}
@@ -84,8 +90,8 @@ export async function upgradeEntry(serverUrl: string) {
const error = configAndLicense.error || entryData.error;
if (!error) {
DatabaseManager.updateServerIdentifier(serverUrl, configAndLicense.config!.DiagnosticId);
DatabaseManager.setActiveServerDatabase(serverUrl);
await DatabaseManager.updateServerIdentifier(serverUrl, configAndLicense.config!.DiagnosticId);
await DatabaseManager.setActiveServerDatabase(serverUrl);
deleteV1Data();
}

View File

@@ -544,9 +544,15 @@ export const fetchPostAuthors = async (serverUrl: string, posts: Post[], fetchOn
}
if (promises.length) {
const result = await Promise.all(promises);
const authors = result.flat();
const authorsResult = await Promise.allSettled(promises);
const result = authorsResult.reduce<UserProfile[][]>((acc, item) => {
if (item.status === 'fulfilled') {
acc.push(item.value);
}
return acc;
}, []);
const authors = result.flat();
if (!fetchOnly && authors.length) {
await operator.handleUsers({
users: authors,

View File

@@ -164,8 +164,13 @@ export async function handleNewPostEvent(serverUrl: string, msg: WebSocketMessag
}
}
let actionType: string = ActionType.POSTS.RECEIVED_NEW;
if (isCRTEnabled && post.root_id) {
actionType = ActionType.POSTS.RECEIVED_IN_THREAD;
}
const postModels = await operator.handlePosts({
actionType: ActionType.POSTS.RECEIVED_NEW,
actionType,
order: [post.id],
posts: [post],
prepareRecordsOnly: true,
@@ -203,8 +208,14 @@ export async function handlePostEdited(serverUrl: string, msg: WebSocketMessage)
models.push(...authorsModels);
}
let actionType: string = ActionType.POSTS.RECEIVED_NEW;
const isCRTEnabled = await getIsCRTEnabled(operator.database);
if (isCRTEnabled && post.root_id) {
actionType = ActionType.POSTS.RECEIVED_IN_THREAD;
}
const postModels = await operator.handlePosts({
actionType: ActionType.POSTS.RECEIVED_NEW,
actionType,
order: [post.id],
posts: [post],
prepareRecordsOnly: true,

View File

@@ -2,12 +2,22 @@
// See LICENSE.txt for license information.
import {markTeamThreadsAsRead, processReceivedThreads, updateThread} from '@actions/local/thread';
import {getCurrentTeamId} from '@app/queries/servers/system';
import DatabaseManager from '@database/manager';
import EphemeralStore from '@store/ephemeral_store';
export async function handleThreadUpdatedEvent(serverUrl: string, msg: WebSocketMessage): Promise<void> {
try {
const database = DatabaseManager.serverDatabases[serverUrl]?.database;
if (!database) {
return;
}
const thread: Thread = JSON.parse(msg.data.thread);
const teamId = msg.broadcast.team_id;
let teamId = msg.broadcast.team_id;
if (!teamId) {
teamId = await getCurrentTeamId(database);
}
// Mark it as following
thread.is_following = true;

View File

@@ -33,6 +33,7 @@ export default class WebSocketClient {
private firstConnectCallback?: () => void;
private missedEventsCallback?: () => void;
private reconnectCallback?: () => void;
private reliableReconnectCallback?: () => void;
private errorCallback?: Function;
private closeCallback?: (connectFailCount: number, lastDisconnect: number) => void;
private connectingCallback?: () => void;
@@ -148,8 +149,11 @@ export default class WebSocketClient {
logInfo('websocket re-established connection to', this.url);
if (!reliableWebSockets && this.reconnectCallback) {
this.reconnectCallback();
} else if (reliableWebSockets && this.serverSequence && this.missedEventsCallback) {
this.missedEventsCallback();
} else if (reliableWebSockets) {
this.reliableReconnectCallback?.();
if (this.serverSequence && this.missedEventsCallback) {
this.missedEventsCallback();
}
}
} else if (this.firstConnectCallback) {
logInfo('websocket connected to', this.url);
@@ -295,6 +299,10 @@ export default class WebSocketClient {
this.reconnectCallback = callback;
}
public setReliableReconnectCallback(callback: () => void) {
this.reliableReconnectCallback = callback;
}
public setErrorCallback(callback: Function) {
this.errorCallback = callback;
}

View File

@@ -106,6 +106,7 @@ const ConnectionBanner = ({
}
return () => {
clearTimeoutRef(openTimeout);
clearTimeoutRef(closeTimeout);
};
}, []);
@@ -125,7 +126,7 @@ const ConnectionBanner = ({
}
}, [isConnected]);
useEffect(() => {
useDidUpdate(() => {
if (appState === 'active') {
if (!isConnected && !visible) {
if (!openTimeout.current) {
@@ -138,10 +139,11 @@ const ConnectionBanner = ({
}
}
} else {
setVisible(false);
clearTimeoutRef(openTimeout);
clearTimeoutRef(closeTimeout);
}
}, [appState]);
}, [appState === 'active']);
useEffect(() => {
height.value = withTiming(visible ? ANNOUNCEMENT_BAR_HEIGHT : 0, {
@@ -149,12 +151,6 @@ const ConnectionBanner = ({
});
}, [visible]);
useEffect(() => {
return () => {
clearTimeoutRef(closeTimeout);
};
});
const bannerStyle = useAnimatedStyle(() => ({
height: height.value,
}));

View File

@@ -85,7 +85,7 @@ const MarkdownCodeBlock = ({language = '', content, textStyle}: MarkdownCodeBloc
const screen = Screens.CODE;
const passProps = {
code: content,
language,
language: getHighlightLanguageFromNameOrAlias(language),
textStyle,
};

View File

@@ -142,20 +142,23 @@ export const MODAL_SCREENS_WITHOUT_BACK = new Set<string>([
EDIT_POST,
EDIT_PROFILE,
EDIT_SERVER,
EMOJI_PICKER,
FIND_CHANNELS,
GALLERY,
PERMALINK,
REACTIONS,
]);
export const SCREENS_WITH_TRANSPARENT_BACKGROUND = new Set<string>([
PERMALINK,
REVIEW_APP,
SNACK_BAR,
]);
export const SCREENS_AS_BOTTOM_SHEET = new Set<string>([
BOTTOM_SHEET,
EMOJI_PICKER,
POST_OPTIONS,
THREAD_OPTIONS,
PERMALINK,
REACTIONS,
SNACK_BAR,
USER_PROFILE,
]);

View File

@@ -22,6 +22,7 @@ import AppDataOperator from '@database/operator/app_data_operator';
import ServerDataOperator from '@database/operator/server_data_operator';
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';
@@ -63,14 +64,17 @@ class DatabaseManager {
*/
public init = async (serverUrls: string[]): Promise<void> => {
await this.createAppDatabase();
const buildNumber = DeviceInfo.getBuildNumber();
const versionNumber = DeviceInfo.getVersion();
await beforeUpgrade.call(this, serverUrls, versionNumber, buildNumber);
for await (const serverUrl of serverUrls) {
await this.initServerDatabase(serverUrl);
}
this.appDatabase?.operator.handleInfo({
info: [{
build_number: DeviceInfo.getBuildNumber(),
build_number: buildNumber,
created_at: Date.now(),
version_number: DeviceInfo.getVersion(),
version_number: versionNumber,
}],
prepareRecordsOnly: false,
});
@@ -428,7 +432,7 @@ class DatabaseManager {
*/
private deleteServerDatabaseFiles = async (serverUrl: string): Promise<void> => {
const databaseName = urlSafeBase64Encode(serverUrl);
this.deleteServerDatabaseFilesByName(databaseName);
return this.deleteServerDatabaseFilesByName(databaseName);
};
/**
@@ -439,7 +443,7 @@ class DatabaseManager {
private deleteServerDatabaseFilesByName = async (databaseName: string): Promise<void> => {
if (Platform.OS === 'ios') {
// On iOS, we'll delete the *.db file under the shared app-group/databases folder
deleteIOSDatabase({databaseName});
await deleteIOSDatabase({databaseName});
return;
}
@@ -449,14 +453,15 @@ class DatabaseManager {
const databaseShm = `${androidFilesDir}${databaseName}.db-shm`;
const databaseWal = `${androidFilesDir}${databaseName}.db-wal`;
FileSystem.unlink(databaseFile).catch(emptyFunction);
FileSystem.unlink(databaseShm).catch(emptyFunction);
FileSystem.unlink(databaseWal).catch(emptyFunction);
await FileSystem.unlink(databaseFile).catch(emptyFunction);
await FileSystem.unlink(databaseShm).catch(emptyFunction);
await FileSystem.unlink(databaseWal).catch(emptyFunction);
};
/**
* deleteServerDatabaseFilesByName: Removes the *.db file from the App-Group directory for iOS or the files directory for Android, given the database name
* renameDatabase: Renames the *.db file from the App-Group directory for iOS or the files directory for Android
* @param {string} databaseName
* @param {string} newDBName
* @returns {Promise<void>}
*/
private renameDatabase = async (databaseName: string, newDBName: string): Promise<void> => {

View File

@@ -4,105 +4,6 @@
// NOTE : To implement migration, please follow this document
// https://nozbe.github.io/WatermelonDB/Advanced/Migrations.html
import {schemaMigrations, addColumns, createTable} from '@nozbe/watermelondb/Schema/migrations';
import {schemaMigrations} from '@nozbe/watermelondb/Schema/migrations';
import {MM_TABLES} from '@constants/database';
import {tableSchemaSpec as configSpec} from '@database/schema/server/table_schemas/config';
import {tableSchemaSpec as teamThreadsSyncSpec} from '@database/schema/server/table_schemas/team_threads_sync';
import {tableSchemaSpec as threadSpec} from '@database/schema/server/table_schemas/thread';
import {tableSchemaSpec as threadInTeamSpec} from '@database/schema/server/table_schemas/thread_in_team';
import {tableSchemaSpec as threadParticipantSpec} from '@database/schema/server/table_schemas/thread_participant';
const {SERVER: {
GROUP,
MY_CHANNEL,
TEAM,
THREAD,
THREAD_PARTICIPANT,
THREADS_IN_TEAM,
USER,
}} = MM_TABLES;
export default schemaMigrations({migrations: [
{
toVersion: 7,
steps: [
// Along with adding the new table - TeamThreadsSync,
// We need to clear the data in thread related tables (DROP & CREATE) to fetch the fresh data from the server
createTable({
...teamThreadsSyncSpec,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
unsafeSql: (baseSql) => {
return `
${baseSql}
DROP TABLE ${THREAD};
DROP TABLE ${THREADS_IN_TEAM};
DROP TABLE ${THREAD_PARTICIPANT};
`;
},
}),
createTable(threadSpec),
createTable(threadInTeamSpec),
createTable(threadParticipantSpec),
],
},
{
toVersion: 6,
steps: [
addColumns({
table: USER,
columns: [
{name: 'terms_of_service_id', type: 'string'},
{name: 'terms_of_service_create_at', type: 'number'},
],
}),
],
},
{
toVersion: 5,
steps: [
createTable(configSpec),
],
},
{
toVersion: 4,
steps: [
addColumns({
table: TEAM,
columns: [
{name: 'invite_id', type: 'string'},
],
}),
],
},
{
toVersion: 3,
steps: [
addColumns({
table: GROUP,
columns: [
{name: 'member_count', type: 'number'},
],
}),
],
},
{
toVersion: 2,
steps: [
addColumns({
table: MY_CHANNEL,
columns: [
{name: 'last_fetched_at', type: 'number', isIndexed: true},
],
}),
addColumns({
table: THREAD,
columns: [
{name: 'last_fetched_at', type: 'number', isIndexed: true},
],
}),
],
},
]});
export default schemaMigrations({migrations: []});

View File

@@ -39,7 +39,7 @@ import {
} from './table_schemas';
export const serverSchema: AppSchema = appSchema({
version: 7,
version: 1,
tables: [
CategorySchema,
CategoryChannelSchema,

View File

@@ -45,7 +45,7 @@ const {
describe('*** Test schema for SERVER database ***', () => {
it('=> The SERVER SCHEMA should strictly match', () => {
expect(serverSchema).toEqual({
version: 7,
version: 1,
unsafeSql: undefined,
tables: {
[CATEGORY]: {

View File

@@ -0,0 +1,30 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {getLastInstalledVersion} from '@queries/app/info';
import {logError, logInfo} from '@utils/log';
import type {DatabaseManager} from '@typings/database/manager';
import type InfoModel from '@typings/database/models/app/info';
export async function beforeUpgrade(serverUrls: string[], versionNumber: string, buildNumber: string) {
const info = await getLastInstalledVersion();
const manager: DatabaseManager | undefined = this.serverDatabases ? this : undefined;
if (manager && serverUrls.length && info && (versionNumber !== info.versionNumber || buildNumber !== info.buildNumber)) {
await beforeUpgradeTo450(manager, serverUrls, info);
}
}
async function beforeUpgradeTo450(manager: DatabaseManager, serverUrls: string[], info: InfoModel) {
try {
const buildNumber = parseInt(info.buildNumber, 10);
if (info.versionNumber === '2.0.0' && buildNumber < 450) {
for await (const serverUrl of serverUrls) {
logInfo('Remove database before upgrading for', serverUrl);
await manager.deleteServerDatabaseFiles(serverUrl);
}
}
} catch (e) {
logError('Error running the upgrade before build 450', e);
}
}

View File

@@ -47,11 +47,11 @@ export async function initialize() {
export async function start() {
await initialize();
await WebsocketManager.init(serverCredentials);
PushNotifications.init(serverCredentials.length > 0);
registerNavigationListeners();
registerScreens();
initialLaunch();
await initialLaunch();
WebsocketManager.init(serverCredentials);
}

View File

@@ -27,7 +27,7 @@ const initialNotificationTypes = [PushNotification.NOTIFICATION_TYPE.MESSAGE, Pu
export const initialLaunch = async () => {
const deepLinkUrl = await Linking.getInitialURL();
if (deepLinkUrl) {
launchAppFromDeepLink(deepLinkUrl, true);
await launchAppFromDeepLink(deepLinkUrl, true);
return;
}
@@ -43,21 +43,21 @@ export const initialLaunch = async () => {
tapped = delivered.find((d) => (d as unknown as NotificationData).ack_id === notification?.payload.ack_id) == null;
}
if (initialNotificationTypes.includes(notification?.payload?.type) && tapped) {
launchAppFromNotification(convertToNotificationData(notification!), true);
await launchAppFromNotification(convertToNotificationData(notification!), true);
return;
}
launchApp({launchType: Launch.Normal, coldStart: true});
await launchApp({launchType: Launch.Normal, coldStart: true});
};
const launchAppFromDeepLink = (deepLinkUrl: string, coldStart = false) => {
const launchAppFromDeepLink = async (deepLinkUrl: string, coldStart = false) => {
const props = getLaunchPropsFromDeepLink(deepLinkUrl, coldStart);
launchApp(props);
return launchApp(props);
};
const launchAppFromNotification = async (notification: NotificationWithData, coldStart = false) => {
const props = await getLaunchPropsFromNotification(notification, coldStart);
launchApp(props);
return launchApp(props);
};
/**

View File

@@ -28,7 +28,9 @@ export class Analytics {
};
async init(config: ClientConfig) {
this.analytics = require('@rudderstack/rudder-sdk-react-native').default;
if (LocalConfig.RudderApiKey) {
this.analytics = require('@rudderstack/rudder-sdk-react-native').default;
}
if (this.analytics) {
const {height, width} = Dimensions.get('window');
@@ -123,10 +125,18 @@ export class Analytics {
}
trackAPI(event: string, props?: any) {
if (!this.analytics) {
return;
}
this.trackEvent('api', event, props);
}
trackCommand(event: string, command: string, errorMessage?: string) {
if (!this.analytics) {
return;
}
const sanitizedCommand = this.sanitizeCommand(command);
let props: any;
if (errorMessage) {
@@ -139,6 +149,9 @@ export class Analytics {
}
trackAction(event: string, props?: any) {
if (!this.analytics) {
return;
}
this.trackEvent('action', event, props);
}

View File

@@ -78,8 +78,9 @@ class SessionManager {
};
private clearCookiesForServer = async (serverUrl: string) => {
this.clearCookies(serverUrl, false);
if (Platform.OS === 'ios') {
this.clearCookies(serverUrl, false);
// Also delete any cookies that were set by react-native-webview
this.clearCookies(serverUrl, true);
} else if (Platform.OS === 'android') {

View File

@@ -30,12 +30,12 @@ class WebsocketManager {
private connectionTimerIDs: Record<string, DebouncedFunc<() => void>> = {};
private isBackgroundTimerRunning = false;
private netConnected = false;
private previousAppState: AppStateStatus;
private previousActiveState: boolean;
private statusUpdatesIntervalIDs: Record<string, NodeJS.Timer> = {};
private backgroundIntervalId: number | undefined;
constructor() {
this.previousAppState = AppState.currentState;
this.previousActiveState = AppState.currentState === 'active';
}
public init = async (serverCredentials: ServerCredential[]) => {
@@ -81,6 +81,7 @@ class WebsocketManager {
//client.setMissedEventsCallback(() => {}) Nothing to do on missedEvents callback
client.setReconnectCallback(() => this.onReconnect(serverUrl));
client.setReliableReconnectCallback(() => this.onReliableReconnect(serverUrl));
client.setCloseCallback((connectFailCount: number, lastDisconnect: number) => this.onWebsocketClose(serverUrl, connectFailCount, lastDisconnect));
if (this.netConnected && ['unknown', 'active'].includes(AppState.currentState)) {
@@ -153,23 +154,27 @@ class WebsocketManager {
private onFirstConnect = (serverUrl: string) => {
this.startPeriodicStatusUpdates(serverUrl);
handleFirstConnect(serverUrl);
this.getConnectedSubject(serverUrl).next('connected');
handleFirstConnect(serverUrl);
};
private onReconnect = async (serverUrl: string) => {
this.startPeriodicStatusUpdates(serverUrl);
this.getConnectedSubject(serverUrl).next('connected');
await handleReconnect(serverUrl);
};
private onReliableReconnect = async (serverUrl: string) => {
this.getConnectedSubject(serverUrl).next('connected');
};
private onWebsocketClose = async (serverUrl: string, connectFailCount: number, lastDisconnect: number) => {
this.getConnectedSubject(serverUrl).next('not_connected');
if (connectFailCount <= 1) { // First fail
await setCurrentUserStatusOffline(serverUrl);
await handleClose(serverUrl, lastDisconnect);
this.stopPeriodicStatusUpdates(serverUrl);
this.getConnectedSubject(serverUrl).next('not_connected');
}
};
@@ -205,14 +210,15 @@ class WebsocketManager {
}
private onAppStateChange = async (appState: AppStateStatus) => {
if (appState === this.previousAppState) {
const isActive = appState === 'active';
if (isActive === this.previousActiveState) {
return;
}
const isMain = isMainActivity();
this.cancelAllConnections();
if (appState !== 'active' && !this.isBackgroundTimerRunning) {
if (!isActive && !this.isBackgroundTimerRunning) {
this.isBackgroundTimerRunning = true;
this.cancelAllConnections();
this.backgroundIntervalId = BackgroundTimer.setInterval(() => {
@@ -221,22 +227,22 @@ class WebsocketManager {
this.isBackgroundTimerRunning = false;
}, WAIT_TO_CLOSE);
this.previousAppState = appState;
this.previousActiveState = isActive;
return;
}
if (appState === 'active' && this.netConnected && isMain) { // Reopen the websockets only if there is connection
if (isActive && this.netConnected && isMain) { // Reopen the websockets only if there is connection
if (this.backgroundIntervalId) {
BackgroundTimer.clearInterval(this.backgroundIntervalId);
}
this.isBackgroundTimerRunning = false;
this.openAll();
this.previousAppState = appState;
this.previousActiveState = isActive;
return;
}
if (isMain) {
this.previousAppState = appState;
this.previousActiveState = isActive;
}
};
@@ -248,7 +254,7 @@ class WebsocketManager {
this.netConnected = newState;
if (this.netConnected && this.previousAppState === 'active') { // Reopen the websockets only if the app is active
if (this.netConnected && this.previousActiveState) { // Reopen the websockets only if the app is active
this.openAll();
return;
}

24
app/queries/app/info.ts Normal file
View File

@@ -0,0 +1,24 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Q} from '@nozbe/watermelondb';
import {MM_TABLES} from '@constants/database';
import DatabaseManager from '@database/manager';
import type InfoModel from '@typings/database/models/app/info';
const {APP: {INFO}} = MM_TABLES;
export const getLastInstalledVersion = async () => {
try {
const {database} = DatabaseManager.getAppDatabaseAndOperator();
const infos = await database.get<InfoModel>(INFO).query(
Q.sortBy('created_at', Q.desc),
Q.take(1),
).fetch();
return infos[0];
} catch {
return undefined;
}
};

View File

@@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import React, {Dispatch, RefObject, SetStateAction, useCallback} from 'react';
import {StyleSheet} from 'react-native';
import {Platform, StyleSheet} from 'react-native';
import OptionItem from '@components/option_item';
import {SearchRef} from '@components/search';
@@ -33,6 +33,12 @@ const Modifier = ({item, searchRef, searchValue, setSearchValue}: Props) => {
addModifierTerm(item.term);
}, [item.term, searchValue]);
const setNativeCursorPositionProp = (position?: number) => {
setTimeout(() => {
searchRef.current?.setNativeProps({selection: {start: position, end: position}});
}, 50);
};
const addModifierTerm = preventDoubleTap((modifierTerm) => {
let newValue = '';
if (!searchValue) {
@@ -46,9 +52,16 @@ const Modifier = ({item, searchRef, searchValue, setSearchValue}: Props) => {
setSearchValue(newValue);
if (item.cursorPosition) {
const position = newValue.length + item.cursorPosition;
setTimeout(() => {
searchRef.current?.setNativeProps({selection: {start: position, end: position}});
}, 50);
setNativeCursorPositionProp(position);
if (Platform.OS === 'android') {
// on Android the selection set by setNativeProps is permanent thus the caret returns to the same
// position after we stop typing for a few ms. By setting the position to undefined,
// then the caret remains in place.
setTimeout(() => {
setNativeCursorPositionProp(undefined);
}, 50);
}
}
});

View File

@@ -219,11 +219,7 @@ Appearance.addChangeListener(() => {
});
export function getThemeFromState(): Theme {
if (EphemeralStore.theme) {
return EphemeralStore.theme;
}
return getDefaultThemeByAppearance();
return EphemeralStore.theme || getDefaultThemeByAppearance();
}
// This is a temporary helper function to avoid
@@ -479,12 +475,17 @@ export function goToScreen(name: string, title: string, passProps = {}, options
});
}
export function popTopScreen(screenId?: string) {
if (screenId) {
Navigation.pop(screenId);
} else {
const componentId = NavigationStore.getVisibleScreen();
Navigation.pop(componentId);
export async function popTopScreen(screenId?: string) {
try {
if (screenId) {
await Navigation.pop(screenId);
} else {
const componentId = NavigationStore.getVisibleScreen();
await Navigation.pop(componentId);
}
} catch (error) {
// RNN returns a promise rejection if there are no screens
// atop the root screen to pop. We'll do nothing in this case.
}
}

View File

@@ -36,12 +36,12 @@ export const getIOSAppGroupDetails = (): IOSAppGroupDetails => {
* e.g :
* MattermostManaged.deleteDatabaseDirectory(databaseName, shouldRemoveDirectory, (error: any, success: any) => { });
*/
export const deleteIOSDatabase = ({
export const deleteIOSDatabase = async ({
databaseName = undefined,
shouldRemoveDirectory = false,
}: IOSDeleteDatabase) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
MattermostManaged.deleteDatabaseDirectory(databaseName, shouldRemoveDirectory, () => null);
return MattermostManaged.deleteDatabaseDirectory(databaseName, shouldRemoveDirectory, () => null);
};
/**

View File

@@ -2,7 +2,6 @@
// See LICENSE.txt for license information.
import {Database} from '@nozbe/watermelondb';
import Sentry from '@sentry/react-native';
import {Breadcrumb, Event} from '@sentry/types';
import {Platform} from 'react-native';
import {Navigation} from 'react-native-navigation';
@@ -19,11 +18,16 @@ import {logError, logWarning} from './log';
export const BREADCRUMB_UNCAUGHT_APP_ERROR = 'uncaught-app-error';
export const BREADCRUMB_UNCAUGHT_NON_ERROR = 'uncaught-non-error';
let Sentry: any;
export function initializeSentry() {
if (!Config.SentryEnabled) {
return;
}
if (!Sentry) {
Sentry = require('@sentry/react-native');
}
const dsn = getDsn();
if (!dsn) {

View File

@@ -1,12 +1,13 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import deepEqual from 'deep-equal';
import merge from 'deepmerge';
import {StatusBar, StyleSheet} from 'react-native';
import tinyColor from 'tinycolor2';
import {Preferences} from '@constants';
import {MODAL_SCREENS_WITHOUT_BACK, SCREENS_WITH_TRANSPARENT_BACKGROUND} from '@constants/screens';
import {MODAL_SCREENS_WITHOUT_BACK, SCREENS_AS_BOTTOM_SHEET, SCREENS_WITH_TRANSPARENT_BACKGROUND} from '@constants/screens';
import EphemeralStore from '@store/ephemeral_store';
import NavigationStore from '@store/navigation_store';
import {NamedStyles} from '@typings/global/styles';
@@ -99,13 +100,25 @@ export function setNavigatorStyles(componentId: string, theme: Theme, additional
},
};
if (!SCREENS_WITH_TRANSPARENT_BACKGROUND.has(componentId)) {
if (SCREENS_AS_BOTTOM_SHEET.has(componentId)) {
options.topBar = {
leftButtonColor: changeOpacity(theme.centerChannelColor, 0.56),
background: {
color: theme.centerChannelBg,
},
title: {
color: theme.centerChannelColor,
},
};
}
if (!SCREENS_WITH_TRANSPARENT_BACKGROUND.has(componentId) && !SCREENS_AS_BOTTOM_SHEET.has(componentId)) {
options.layout = {
componentBackgroundColor: theme.centerChannelBg,
};
}
if (!MODAL_SCREENS_WITHOUT_BACK.has(componentId) && options.topBar) {
if (!MODAL_SCREENS_WITHOUT_BACK.has(componentId) && !SCREENS_AS_BOTTOM_SHEET.has(componentId) && options.topBar) {
options.topBar.backButton = {
color: theme.sidebarHeaderTextColor,
};
@@ -263,7 +276,8 @@ export function setThemeDefaults(theme: ExtendedTheme): Theme {
}
export const updateThemeIfNeeded = (theme: Theme, force = false) => {
if (theme !== EphemeralStore.theme || force) {
const storedTheme = EphemeralStore.theme;
if (!deepEqual(theme, storedTheme) || force) {
EphemeralStore.theme = theme;
requestAnimationFrame(() => {
setNavigationStackStyles(theme);

View File

@@ -563,7 +563,6 @@
"mobile.post.delete_title": "Delete Post",
"mobile.post.failed_delete": "Delete Message",
"mobile.post.failed_retry": "Try Again",
"mobile.posts_view.moreMsg": "More New Messages Above",
"mobile.privacy_link": "Privacy Policy",
"mobile.push_notification_reply.button": "Send",
"mobile.push_notification_reply.placeholder": "Write a reply...",

View File

@@ -552,5 +552,7 @@
"channel_info.edit_header": "제목글 편집",
"channel_info.custom_status": "사용자 정의 상태:",
"channel_info.copy_link": "링크 복사",
"general_settings.display": "화면"
"general_settings.display": "화면",
"intro.group_message": "여기에서 이 그룹과의 대화가 시작됩니다. 여기에 공유된 메시지와 파일은 다른 사람에게는 보이지 않습니다.",
"intro.direct_message": "이곳에서 {teammate}님과의 대화가 시작됩니다. 여기에 공유된 메시지와 파일은 다른 사람에게는 보이지 않습니다."
}

View File

@@ -280,7 +280,7 @@
"mobile.reset_status.title_ooo": "\"Ofis dışında\" devre dışı bırakılsın mı?",
"mobile.routes.code": "{language} kodu",
"mobile.routes.code.noLanguage": "Kod",
"mobile.routes.custom_status": "Bir durum ayarlayın",
"mobile.routes.custom_status": "Bir özel durum ayarlayın",
"mobile.routes.table": "Tablo",
"mobile.routes.user_profile": "Profil",
"mobile.search.jump": "Son iletilere git",
@@ -467,7 +467,7 @@
"display_settings.clock.military": "24 saat",
"custom_status.suggestions.title": "Öneriler",
"custom_status.suggestions.recent_title": "Son kullanılanlar",
"custom_status.set_status": "Bir durum ayarlayın",
"custom_status.set_status": "Bir özel durum ayarlayın",
"create_post.thread_reply": "Bu konuyu yanıtla...",
"create_direct_message.title": "Doğrudan ileti oluştur",
"channel_modal.purposeEx": "Hataların ve geliştirmelerin bildirileceği bir kanal",
@@ -644,7 +644,7 @@
"server_upgrade.dismiss": "Yok say",
"server_upgrade.alert_description": "{serverDisplayName} sunucunuz desteklenmeyen bir sunucu sürümü kullanıyor. Kullanıcılar uyumluluk sorunlarından kaynaklanan çökmeler ya da uygulamanın temel işlevselliğini bozan ciddi sorunlar ile karşılaşacak. Sunucu sürümünün {supportedServerVersion} ya da üzerindeki bir sürüme güncellenmesi gerekli.",
"your.servers": "Sunucularınız",
"video.failed_description": "Görüntü oynatılmaya çalışılırken bir sorun çıktı.\n",
"video.failed_description": "Görüntü oynatılmaya çalışılırken bir sorun çıktı.",
"video.download": "Görüntüyü indir",
"user.tutorial.long_press": "Bir kullanıcının profilini görüntülemek için bir ögeye uzun dokunun",
"user.settings.notifications.email_threads.description": "İzlediğim konulara yazılan tüm yanıtlar ile ilgili bildirim gönderilsin",
@@ -838,12 +838,44 @@
"connection_banner.connected": "Bağlantı yeniden kuruldu",
"mobile.calls_host": "sunucu",
"mobile.calls_dismiss": "Yok say",
"mobile.calls_call_thread": "Kanalı",
"mobile.calls_call_thread": "Çağrı konusu",
"mobile.announcement_banner.title": "Duyuru",
"integration_selector.multiselect.submit": "Bitti",
"extension.no_memberships.description": "İçerik paylaşabilmek için bir Mattermost sunucusundaki bir takımın üyesi olmalısınız.",
"general_settings.report_problem": "Sorun bildirin",
"extension.no_servers.title": "Herhangi bir sunucu ile bağlantı kurulmamış",
"extension.no_servers.description": "İçerik paylaşabilmek için bir Mattermost sunucusundaki oturum açmış olmalısınız.",
"extension.no_memberships.title": "Henüz herhangi bir takımın üyesi değil"
"extension.no_memberships.title": "Henüz herhangi bir takımın üyesi değil",
"onboarding.integrations": "Sevdiğiniz araçlarla bütünleştirin",
"notification_settings.mentions.keywords_mention": "Anmaları tetikleyecek sözcükler",
"notification_settings.auto_responder.message": "İleti",
"mobile.open_dm.error": "{displayName} ile doğrudan ileti açılamadı. Lütfen bağlantınızı denetleyip yeniden deneyin.",
"mobile.onboarding.sign_in_to_get_started": "Başlamak için oturum açın",
"mobile.onboarding.sign_in": "Oturum aç",
"mobile.onboarding.next": "Sonraki",
"mobile.integration_selector.loading_options": "Ayarlar yükleniyor...",
"mobile.integration_selector.loading_channels": "Kanallar yükleniyor...",
"mobile.custom_list.no_results": "Herhangi bir sonuç bulunamadı",
"mobile.create_direct_message.max_limit_reached": "Grup iletileri {maxCount} üye ile sınırlıdır",
"mobile.camera_type.title": "Kamera ayarları",
"mobile.calls_stop_recording": "Kaydı durdur",
"mobile.calls_request_title": "Çağrılar şu anda etkinleştirilmemiş",
"mobile.calls_request_message": "Çağrılar şu anda deneme kipinde çalışıyor ve bunları yalnızca sistem yöneticileri başlatabilir. Yardım almak için doğrudan sistem yöneticinizle görüşün",
"mobile.calls_record": "Kaydet",
"mobile.calls_rec": "kayıt",
"mobile.calls_react": "Tepki ver",
"mobile.calls_host_rec_title": "Kaydediyorsunuz",
"mobile.calls_participant_rec_title": "Kaydediliyor",
"mobile.calls_participant_rec": "Toplantı sahibi bu toplantıyı kaydetmeye başladı. Toplantıda kalarak kayıt alınmasına onay vermiş olursunuz.",
"mobile.calls_participant_limit_title_GA": "Bu çağrının kapasitesi doldu",
"mobile.calls_open_channel": "Kanalı aç",
"mobile.calls_okay": "Tamam",
"mobile.calls_limit_msg_GA": "{maxParticipants} üzerinde katılımcılı grup aramaları yapabilmek için Cloud Professional ya da Cloud Enterprise tarifesine yükseltin.",
"mobile.calls_host_rec_stopped_title": "Kayıt durduruldu. İşleniyor...",
"mobile.calls_host_rec_stopped": "İşlenmesi tamamlandığında, kaydı bu görüşmenin sohbet konusunda bulabilirsiniz.",
"mobile.calls_host_rec": "Bu toplantıyı kaydediyorsunuz. Bu toplantının kaydedildiğinin herkese bildirilmesi değerlendirin.",
"join_team.error.title": "Bir takıma katılınırken sorun çıktı",
"join_team.error.message": "Takıma katılınırken bir sorun çıktı",
"join_team.error.group_error": "Bu takıma katılmak için ilişkilendirilmiş bir grubun üyesi olmalısınız.",
"connection_banner.connecting": "Bağlantı kuruluyor..."
}

View File

@@ -1116,7 +1116,7 @@
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 448;
CURRENT_PROJECT_VERSION = 452;
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
ENABLE_BITCODE = NO;
HEADER_SEARCH_PATHS = (
@@ -1160,7 +1160,7 @@
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 448;
CURRENT_PROJECT_VERSION = 452;
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
ENABLE_BITCODE = NO;
HEADER_SEARCH_PATHS = (
@@ -1303,7 +1303,7 @@
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 448;
CURRENT_PROJECT_VERSION = 452;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
GCC_C_LANGUAGE_STANDARD = gnu11;
@@ -1354,7 +1354,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 448;
CURRENT_PROJECT_VERSION = 452;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
GCC_C_LANGUAGE_STANDARD = gnu11;

View File

@@ -37,7 +37,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>448</string>
<string>452</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>

View File

@@ -21,7 +21,7 @@
<key>CFBundleShortVersionString</key>
<string>2.0.0</string>
<key>CFBundleVersion</key>
<string>448</string>
<string>452</string>
<key>UIAppFonts</key>
<array>
<string>OpenSans-Bold.ttf</string>

View File

@@ -21,7 +21,7 @@
<key>CFBundleShortVersionString</key>
<string>2.0.0</string>
<key>CFBundleVersion</key>
<string>448</string>
<string>452</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>

View File

@@ -157,7 +157,7 @@ class NotificationService: UNNotificationServiceExtension {
os_log(
OSLogType.default,
"Mattermost Notifications: notification receipt failed with status %{public}@. Will call sendMessageIntent",
httpResponse.statusCode
String(describing: httpResponse.statusCode)
)
self.sendMessageIntent(notification: self.bestAttemptContent!)
return
@@ -172,7 +172,7 @@ class NotificationService: UNNotificationServiceExtension {
os_log(
OSLogType.default,
"Mattermost Notifications: receipt retrieval failed. Retry %{public}@",
self.retryIndex
String(describing: self.retryIndex)
)
self.fetchReceipt(ackNotification)
})

45
types/database/manager.ts Normal file
View File

@@ -0,0 +1,45 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {Database} from '@nozbe/watermelondb';
import type {AppDatabase, ServerDatabase, ServerDatabases} from '@typings/database/database';
export type DatabaseManager = {
serverDatabases: ServerDatabases;
updateServerIdentifier: (serverUrl: string, identifier: string, displayName?: string) => Promise<void>;
updateServerDisplayName: (serverUrl: string, displayName: string) => Promise<void>;
isServerPresent: (serverUrl: string) => Promise<boolean>;
getActiveServerUrl: () => Promise<string|undefined>;
getActiveServerDisplayName: () => Promise<string|undefined>;
getServerUrlFromIdentifier: (identifier: string) => Promise<string|undefined>;
getActiveServerDatabase: () => Promise<Database|undefined>;
getAppDatabaseAndOperator: () => AppDatabase|undefined;
getServerDatabaseAndOperator: (serverUrl: string) => ServerDatabase | undefined;
setActiveServerDatabase: (serverUrl: string) => Promise<void>;
deleteServerDatabase: (serverUrl: string) => Promise<void>;
destroyServerDatabase: (serverUrl: string) => Promise<void>;
deleteServerDatabaseFiles: (serverUrl: string) => Promise<void>;
deleteServerDatabaseFilesByName: (databaseName: string) => Promise<void>;
renameDatabase: (databaseName: string, newDBName: string) => Promise<void>;
factoryReset: (shouldRemoveDirectory: boolean) => Promise<boolean>;
getDatabaseFilePath: (dbName: string) => string;
searchUrl: (toFind: string) => string | undefined;
}