forked from Ivasoft/mattermost-mobile
Compare commits
13 Commits
release-1.
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc27aa1794 | ||
|
|
bc0a19fde0 | ||
|
|
633085b5c0 | ||
|
|
a8c0f26a71 | ||
|
|
c4b683a58f | ||
|
|
2e37722368 | ||
|
|
18ae9fd11f | ||
|
|
a833a936e2 | ||
|
|
f0390f2463 | ||
|
|
cbdb820cbe | ||
|
|
0bbbbb98cb | ||
|
|
782dfc85dc | ||
|
|
74365b11a7 |
@@ -63,4 +63,4 @@ untyped-import
|
||||
untyped-type-import
|
||||
|
||||
[version]
|
||||
^0.158.0
|
||||
^0.162.0
|
||||
|
||||
54
NOTICE.txt
54
NOTICE.txt
@@ -43,6 +43,25 @@ SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## msgpack/msgpack
|
||||
|
||||
This product contains 'msgpack/msgpack' by MessagePack.
|
||||
|
||||
MessagePack is an efficient binary serialization format. It's like JSON. but fast and small.
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/msgpack/msgpack
|
||||
|
||||
* LICENSE: ISC
|
||||
|
||||
Copyright 2019 The MessagePack Community.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## @react-native-community/async-storage
|
||||
|
||||
This product contains 'async-storage' by Krzysztof Borowy.
|
||||
@@ -1368,6 +1387,41 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## pako
|
||||
|
||||
This product contains 'pako' by Nodeca.
|
||||
|
||||
zlib port to javascript, very fast!
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/nodeca/pako
|
||||
|
||||
* LICENSE: MIT
|
||||
|
||||
(The MIT License)
|
||||
|
||||
Copyright (C) 2014-2017 by Vitaly Puzrin and Andrei Tuputcyn
|
||||
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
## prop-types
|
||||
|
||||
This product contains 'prop-types' by Facebook.
|
||||
|
||||
@@ -113,7 +113,7 @@ def jscFlavor = 'org.webkit:android-jsc-intl:+'
|
||||
/**
|
||||
* Whether to enable the Hermes VM.
|
||||
*
|
||||
* This should be set on project.ext.react and mirrored here. If it is not set
|
||||
* This should be set on project.ext.react and that value will be read here. If it is not set
|
||||
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
|
||||
* and the benefits of using Hermes will therefore be sharply reduced.
|
||||
*/
|
||||
@@ -131,8 +131,8 @@ android {
|
||||
applicationId "com.mattermost.rnbeta"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 382
|
||||
versionName "1.48.2"
|
||||
versionCode 386
|
||||
versionName "1.49.1"
|
||||
multiDexEnabled = true
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
@@ -86,5 +86,9 @@
|
||||
<intent>
|
||||
<action android:name="com.google.android.youtube.api.service.START" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<data android:mimeType="*/*" />
|
||||
</intent>
|
||||
</queries>
|
||||
</manifest>
|
||||
|
||||
@@ -8,13 +8,11 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.swmansion.reanimated.ReanimatedJSIModulePackage;
|
||||
import com.ammarahmed.mmkv.RNMMKVModule;
|
||||
|
||||
public class CustomMMKVJSIModulePackage extends ReanimatedJSIModulePackage {
|
||||
@Override
|
||||
public List<JSIModuleSpec> getJSIModules(ReactApplicationContext reactApplicationContext, JavaScriptContextHolder jsContext) {
|
||||
super.getJSIModules(reactApplicationContext, jsContext);
|
||||
reactApplicationContext.getNativeModule(RNMMKVModule.class).installLib(jsContext, reactApplicationContext.getFilesDir().getAbsolutePath() + "/mmkv");
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
36
android/app/src/main/res/drawable/rn_edit_text_material.xml
Normal file
36
android/app/src/main/res/drawable/rn_edit_text_material.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2014 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:insetLeft="@dimen/abc_edit_text_inset_horizontal_material"
|
||||
android:insetRight="@dimen/abc_edit_text_inset_horizontal_material"
|
||||
android:insetTop="@dimen/abc_edit_text_inset_top_material"
|
||||
android:insetBottom="@dimen/abc_edit_text_inset_bottom_material">
|
||||
|
||||
<selector>
|
||||
<!--
|
||||
This file is a copy of abc_edit_text_material (https://bit.ly/3k8fX7I).
|
||||
The item below with state_pressed="false" and state_focused="false" causes a NullPointerException.
|
||||
NullPointerException:tempt to invoke virtual method 'android.graphics.drawable.Drawable android.graphics.drawable.Drawable$ConstantState.newDrawable(android.content.res.Resources)'
|
||||
|
||||
<item android:state_pressed="false" android:state_focused="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
|
||||
|
||||
For more info, see https://bit.ly/3CdLStv (react-native/pull/29452) and https://bit.ly/3nxOMoR.
|
||||
-->
|
||||
<item android:state_enabled="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
|
||||
<item android:drawable="@drawable/abc_textfield_activated_mtrl_alpha"/>
|
||||
</selector>
|
||||
|
||||
</inset>
|
||||
@@ -4,7 +4,7 @@
|
||||
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="android:windowBackground">@color/splashscreen_bg</item>
|
||||
|
||||
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<files-path name="files" path="." />
|
||||
<external-files-path name="external_files" path="." />
|
||||
<external-path name="external" path="." />
|
||||
<cache-path name="cache" path="." />
|
||||
<root-path name="root" path="." />
|
||||
</paths>
|
||||
@@ -14,14 +14,13 @@ buildscript {
|
||||
|
||||
}
|
||||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.2.2'
|
||||
classpath 'com.google.gms:google-services:4.2.0'
|
||||
classpath 'com.google.gms:google-services:4.3.10'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
@@ -32,9 +31,9 @@ buildscript {
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
jcenter()
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
maven {
|
||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||
url("$rootDir/../node_modules/react-native/android")
|
||||
@@ -49,6 +48,13 @@ allprojects {
|
||||
// prebuilt libv8android.so
|
||||
// url("$rootDir/../node_modules/v8-android/dist")
|
||||
}
|
||||
mavenCentral {
|
||||
// We don't want to fetch react-native from Maven Central as there are
|
||||
// older versions over there.
|
||||
content {
|
||||
excludeGroup "com.facebook.react"
|
||||
}
|
||||
}
|
||||
maven {
|
||||
url "https://www.jitpack.io"
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
||||
# Default value: -Xmx1024m -XX:MaxPermSize=256m
|
||||
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
org.gradle.jvmargs=-Xmx2048M
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
|
||||
|
||||
@@ -19,19 +19,19 @@ import {getPreferenceKey} from '@mm-redux/utils/preference_utils';
|
||||
import {isDirectChannelVisible, isGroupChannelVisible} from '@utils/channels';
|
||||
import {buildPreference} from '@utils/preferences';
|
||||
|
||||
export async function loadSidebarDirectMessagesProfiles(state: GlobalState, channels: Array<Channel>, channelMembers: Array<ChannelMembership>) {
|
||||
export async function loadSidebarDirectMessagesProfiles(state: GlobalState, channels: Channel[], channelMembers: ChannelMembership[]) {
|
||||
const currentUserId = getCurrentUserId(state);
|
||||
const usersInChannel: RelationOneToMany<Channel, UserProfile> = getUserIdsInChannels(state);
|
||||
const directChannels = Object.values(channels).filter((c) => c.type === General.DM_CHANNEL || c.type === General.GM_CHANNEL);
|
||||
const prefs: Array<PreferenceType> = [];
|
||||
const prefs: PreferenceType[] = [];
|
||||
const promises: Array<Promise<ActionResult>> = []; //only fetch profiles that we don't have and the Direct channel should be visible
|
||||
const actions = [];
|
||||
const userIds: Array<string> = [];
|
||||
const userIds: string[] = [];
|
||||
|
||||
// Prepare preferences and start fetching profiles to batch them
|
||||
directChannels.forEach((c) => {
|
||||
const profileIds = Array.from(usersInChannel[c.id] || []);
|
||||
const profilesInChannel: Array<string> = profileIds.filter((u: string) => u !== currentUserId);
|
||||
const profilesInChannel: string[] = profileIds.filter((u: string) => u !== currentUserId);
|
||||
userIds.push(...profilesInChannel);
|
||||
|
||||
switch (c.type) {
|
||||
@@ -118,7 +118,7 @@ export async function fetchMyChannelMember(channelId: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export function markChannelAsUnread(state: GlobalState, teamId: string, channelId: string, mentions: Array<string>, isRoot = false): Array<GenericAction> {
|
||||
export function markChannelAsUnread(state: GlobalState, teamId: string, channelId: string, mentions: string[], isRoot = false): GenericAction[] {
|
||||
const {myMembers} = state.entities.channels;
|
||||
const {currentUserId} = state.entities.users;
|
||||
|
||||
@@ -214,8 +214,8 @@ export async function makeGroupMessageVisibleIfNecessary(state: GlobalState, cha
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchChannelAndMyMember(channelId: string): Promise<Array<GenericAction>> {
|
||||
const actions: Array<GenericAction> = [];
|
||||
export async function fetchChannelAndMyMember(channelId: string): Promise<GenericAction[]> {
|
||||
const actions: GenericAction[] = [];
|
||||
|
||||
try {
|
||||
const [channel, member] = await Promise.all([
|
||||
@@ -246,9 +246,9 @@ export async function fetchChannelAndMyMember(channelId: string): Promise<Array<
|
||||
return actions;
|
||||
}
|
||||
|
||||
export async function getAddedDmUsersIfNecessary(state: GlobalState, preferences: PreferenceType[]): Promise<Array<GenericAction>> {
|
||||
export async function getAddedDmUsersIfNecessary(state: GlobalState, preferences: PreferenceType[]): Promise<GenericAction[]> {
|
||||
const userIds: string[] = [];
|
||||
const actions: Array<GenericAction> = [];
|
||||
const actions: GenericAction[] = [];
|
||||
|
||||
for (const preference of preferences) {
|
||||
if (preference.category === Preferences.CATEGORY_DIRECT_CHANNEL_SHOW && preference.value === 'true') {
|
||||
@@ -319,7 +319,7 @@ export function lastChannelIdForTeam(state: GlobalState, teamId: string): string
|
||||
return firstChannel;
|
||||
}
|
||||
|
||||
function fetchDirectMessageProfileIfNeeded(state: GlobalState, channel: Channel, channelMembers: Array<ChannelMembership>, profilesInChannel: Array<string>) {
|
||||
function fetchDirectMessageProfileIfNeeded(state: GlobalState, channel: Channel, channelMembers: ChannelMembership[], profilesInChannel: string[]) {
|
||||
const currentUserId = getCurrentUserId(state);
|
||||
const myPreferences = getMyPreferences(state);
|
||||
const users = getUsers(state);
|
||||
@@ -351,7 +351,7 @@ function fetchDirectMessageProfileIfNeeded(state: GlobalState, channel: Channel,
|
||||
return {preferences};
|
||||
}
|
||||
|
||||
function fetchGroupMessageProfilesIfNeeded(state: GlobalState, channel: Channel, channelMembers: Array<ChannelMembership>, profilesInChannel: Array<string>) {
|
||||
function fetchGroupMessageProfilesIfNeeded(state: GlobalState, channel: Channel, channelMembers: ChannelMembership[], profilesInChannel: string[]) {
|
||||
const currentUserId = getCurrentUserId(state);
|
||||
const myPreferences = getMyPreferences(state);
|
||||
const config = getConfig(state);
|
||||
|
||||
@@ -3,10 +3,13 @@
|
||||
|
||||
import {intlShape} from 'react-intl';
|
||||
import {Keyboard} from 'react-native';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
|
||||
import {dismissAllModals, showModalOverCurrentContext} from '@actions/navigation';
|
||||
import {showModalOverCurrentContext} from '@actions/navigation';
|
||||
import {loadChannelsByTeamName} from '@actions/views/channel';
|
||||
import {selectFocusedPostId} from '@mm-redux/actions/posts';
|
||||
import {getPost as fetchPost, selectFocusedPostId} from '@mm-redux/actions/posts';
|
||||
import {getPost} from '@mm-redux/selectors/entities/posts';
|
||||
import {isCollapsedThreadsEnabled} from '@mm-redux/selectors/entities/preferences';
|
||||
import {getCurrentTeam} from '@mm-redux/selectors/entities/teams';
|
||||
import {permalinkBadTeam} from '@utils/general';
|
||||
import {changeOpacity} from '@utils/theme';
|
||||
@@ -17,26 +20,50 @@ let showingPermalink = false;
|
||||
|
||||
export function showPermalink(intl: typeof intlShape, teamName: string, postId: string, openAsPermalink = true) {
|
||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||
const state = getState();
|
||||
|
||||
let name = teamName;
|
||||
if (!name) {
|
||||
name = getCurrentTeam(getState()).name;
|
||||
name = getCurrentTeam(state).name;
|
||||
}
|
||||
|
||||
const loadTeam = await dispatch(loadChannelsByTeamName(name, permalinkBadTeam.bind(null, intl)));
|
||||
|
||||
let isThreadPost;
|
||||
|
||||
const collapsedThreadsEnabled = isCollapsedThreadsEnabled(state);
|
||||
if (collapsedThreadsEnabled) {
|
||||
let post = getPost(state, postId);
|
||||
if (!post) {
|
||||
const {data} = await dispatch(fetchPost(postId));
|
||||
if (data) {
|
||||
post = data;
|
||||
}
|
||||
}
|
||||
if (post) {
|
||||
isThreadPost = Boolean(post.root_id);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (!loadTeam.error) {
|
||||
Keyboard.dismiss();
|
||||
dispatch(selectFocusedPostId(postId));
|
||||
if (showingPermalink) {
|
||||
await dismissAllModals();
|
||||
}
|
||||
|
||||
const screen = 'Permalink';
|
||||
const passProps = {
|
||||
isPermalink: openAsPermalink,
|
||||
isThreadPost,
|
||||
focusedPostId: postId,
|
||||
teamName,
|
||||
};
|
||||
|
||||
if (showingPermalink) {
|
||||
Navigation.updateProps(screen, passProps);
|
||||
return {};
|
||||
}
|
||||
|
||||
const options = {
|
||||
layout: {
|
||||
componentBackgroundColor: changeOpacity('#000', 0.2),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import deepEqual from 'deep-equal';
|
||||
import {batchActions} from 'redux-batched-actions';
|
||||
|
||||
import {Client4} from '@client/rest';
|
||||
@@ -31,7 +32,10 @@ export function startDataCleanup() {
|
||||
|
||||
export function loadConfigAndLicense() {
|
||||
return async (dispatch, getState) => {
|
||||
const {currentUserId} = getState().entities.users;
|
||||
const state = getState();
|
||||
const {currentUserId} = state.entities.users;
|
||||
const {general} = state.entities;
|
||||
const actions = [];
|
||||
|
||||
try {
|
||||
const [config, license] = await Promise.all([
|
||||
@@ -39,23 +43,31 @@ export function loadConfigAndLicense() {
|
||||
Client4.getClientLicenseOld(),
|
||||
]);
|
||||
|
||||
const actions = [{
|
||||
type: GeneralTypes.CLIENT_CONFIG_RECEIVED,
|
||||
data: config,
|
||||
}, {
|
||||
type: GeneralTypes.CLIENT_LICENSE_RECEIVED,
|
||||
data: license,
|
||||
}];
|
||||
if (!deepEqual(general.config, config)) {
|
||||
actions.push({
|
||||
type: GeneralTypes.CLIENT_CONFIG_RECEIVED,
|
||||
data: config,
|
||||
});
|
||||
}
|
||||
|
||||
if (currentUserId) {
|
||||
if (license?.IsLicensed === 'true' && license?.DataRetention === 'true') {
|
||||
dispatch(getDataRetentionPolicy());
|
||||
} else {
|
||||
actions.push({type: GeneralTypes.RECEIVED_DATA_RETENTION_POLICY, data: {}});
|
||||
if (!deepEqual(general.license, license)) {
|
||||
actions.push({
|
||||
type: GeneralTypes.CLIENT_LICENSE_RECEIVED,
|
||||
data: license,
|
||||
});
|
||||
|
||||
if (currentUserId) {
|
||||
if (license?.IsLicensed === 'true' && license?.DataRetention === 'true') {
|
||||
dispatch(getDataRetentionPolicy());
|
||||
} else {
|
||||
actions.push({type: GeneralTypes.RECEIVED_DATA_RETENTION_POLICY, data: {}});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dispatch(batchActions(actions, 'BATCH_LOAD_CONFIG_AND_LICENSE'));
|
||||
if (actions.length) {
|
||||
dispatch(batchActions(actions, 'BATCH_LOAD_CONFIG_AND_LICENSE'));
|
||||
}
|
||||
|
||||
return {config, license};
|
||||
} catch (error) {
|
||||
@@ -64,7 +76,7 @@ export function loadConfigAndLicense() {
|
||||
};
|
||||
}
|
||||
|
||||
export function loadFromPushNotification(notification, isInitialNotification) {
|
||||
export function loadFromPushNotification(notification, isInitialNotification, skipChannelSwitch = false) {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const {payload} = notification;
|
||||
@@ -96,12 +108,14 @@ export function loadFromPushNotification(notification, isInitialNotification) {
|
||||
await Promise.all(loading);
|
||||
}
|
||||
|
||||
dispatch(handleSelectTeamAndChannel(teamId, channelId));
|
||||
dispatch(selectPost(''));
|
||||
if (!skipChannelSwitch) {
|
||||
dispatch(handleSelectTeamAndChannel(teamId, channelId));
|
||||
dispatch(selectPost(''));
|
||||
|
||||
const {root_id: rootId} = notification.payload || {};
|
||||
if (isCollapsedThreadsEnabled(state) && rootId) {
|
||||
dispatch(selectPost(rootId));
|
||||
const {root_id: rootId} = notification.payload || {};
|
||||
if (isCollapsedThreadsEnabled(state) && rootId) {
|
||||
dispatch(selectPost(rootId));
|
||||
}
|
||||
}
|
||||
return {data: true};
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import deepEqual from 'deep-equal';
|
||||
import {batchActions} from 'redux-batched-actions';
|
||||
|
||||
import {handleCRTPreferenceChange} from '@actions/views/crt';
|
||||
@@ -121,9 +122,12 @@ export function loadMe(user, deviceToken, skipDispatch = false) {
|
||||
data.teams = teams;
|
||||
data.teamMembers = teamMembers;
|
||||
data.teamUnreads = teamUnreads;
|
||||
data.config = config;
|
||||
data.url = Client4.getUrl();
|
||||
|
||||
if (!deepEqual(state.entities?.general?.config, config)) {
|
||||
data.config = config;
|
||||
}
|
||||
|
||||
actions.push({
|
||||
type: UserTypes.LOGIN,
|
||||
data,
|
||||
|
||||
@@ -62,7 +62,7 @@ export function handleChannelDeletedEvent(msg: WebSocketMessage) {
|
||||
const currentTeamId = getCurrentTeamId(state);
|
||||
const config = getConfig(state);
|
||||
const viewArchivedChannels = config.ExperimentalViewArchivedChannels === 'true';
|
||||
const actions: Array<GenericAction> = [{
|
||||
const actions: GenericAction[] = [{
|
||||
type: ChannelTypes.RECEIVED_CHANNEL_DELETED,
|
||||
data: {
|
||||
id: msg.data.channel_id,
|
||||
@@ -94,7 +94,7 @@ export function handleChannelMemberUpdatedEvent(msg: WebSocketMessage) {
|
||||
try {
|
||||
const channelMember = JSON.parse(msg.data.channelMember);
|
||||
const rolesToLoad = channelMember.roles.split(' ');
|
||||
const actions: Array<GenericAction> = [{
|
||||
const actions: GenericAction[] = [{
|
||||
type: ChannelTypes.RECEIVED_MY_CHANNEL_MEMBER,
|
||||
data: channelMember,
|
||||
}];
|
||||
@@ -129,7 +129,7 @@ export function handleChannelUnarchiveEvent(msg: WebSocketMessage) {
|
||||
const viewArchivedChannels = config.ExperimentalViewArchivedChannels === 'true';
|
||||
|
||||
if (msg.broadcast.team_id === currentTeamId) {
|
||||
const actions: Array<GenericAction> = [{
|
||||
const actions: GenericAction[] = [{
|
||||
type: ChannelTypes.RECEIVED_CHANNEL_UNARCHIVED,
|
||||
data: {
|
||||
id: msg.data.channel_id,
|
||||
|
||||
@@ -6,7 +6,7 @@ import {ActionResult, DispatchFunc, batchActions} from '@mm-redux/types/actions'
|
||||
import {WebSocketMessage} from '@mm-redux/types/websocket';
|
||||
|
||||
export function handleGroupUpdatedEvent(msg: WebSocketMessage) {
|
||||
return (dispatch: DispatchFunc) : ActionResult => {
|
||||
return (dispatch: DispatchFunc): ActionResult => {
|
||||
const data = JSON.parse(msg.data.group);
|
||||
dispatch(batchActions([
|
||||
{
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
handleCallScreenOff,
|
||||
} from '@mmproducts/calls/store/actions/websockets';
|
||||
import {getChannelSinceValue} from '@utils/channels';
|
||||
import {semverFromServerVersion} from '@utils/general';
|
||||
import websocketClient from '@websocket';
|
||||
|
||||
import {handleRefreshAppsBindings} from './apps';
|
||||
@@ -111,7 +112,7 @@ export function doFirstConnect(now: number) {
|
||||
return async (dispatch: DispatchFunc, getState: GetStateFunc): Promise<ActionResult> => {
|
||||
const state = getState();
|
||||
const {lastDisconnectAt} = state.websocket;
|
||||
const actions: Array<GenericAction> = [wsConnected(now)];
|
||||
const actions: GenericAction[] = [wsConnected(now)];
|
||||
|
||||
if (lastDisconnectAt) {
|
||||
const currentUserId = getCurrentUserId(state);
|
||||
@@ -150,7 +151,7 @@ export function doReconnect(now: number) {
|
||||
const currentUserId = getCurrentUserId(state);
|
||||
const users = getUsers(state);
|
||||
const {lastDisconnectAt} = state.websocket;
|
||||
const actions: Array<GenericAction> = [];
|
||||
const actions: GenericAction[] = [];
|
||||
|
||||
dispatch(batchActions([
|
||||
wsConnected(now),
|
||||
@@ -474,11 +475,8 @@ function handleEvent(msg: WebSocketMessage) {
|
||||
}
|
||||
|
||||
function handleHelloEvent(msg: WebSocketMessage) {
|
||||
const serverVersion = msg.data.server_version;
|
||||
if (serverVersion && Client4.serverVersion !== serverVersion) {
|
||||
Client4.serverVersion = serverVersion;
|
||||
EventEmitter.emit(General.SERVER_VERSION_CHANGED, serverVersion);
|
||||
}
|
||||
const serverVersion = semverFromServerVersion(msg.data.server_version);
|
||||
EventEmitter.emit(General.SERVER_VERSION_CHANGED, serverVersion);
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
@@ -43,7 +43,7 @@ export function handleNewPostEvent(msg: WebSocketMessage) {
|
||||
ownPost: data.user_id === currentUserId,
|
||||
};
|
||||
|
||||
const actions: Array<GenericAction> = [];
|
||||
const actions: GenericAction[] = [];
|
||||
|
||||
const exists = selectPost(state, post.pending_post_id);
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import {WebSocketMessage} from '@mm-redux/types/websocket';
|
||||
export function handlePreferenceChangedEvent(msg: WebSocketMessage) {
|
||||
return async (dispatch: DispatchFunc, getState: GetStateFunc): Promise<ActionResult> => {
|
||||
const preference = JSON.parse(msg.data.preference);
|
||||
const actions: Array<GenericAction> = [{
|
||||
const actions: GenericAction[] = [{
|
||||
type: PreferenceTypes.RECEIVED_PREFERENCES,
|
||||
data: [preference],
|
||||
}];
|
||||
@@ -36,7 +36,7 @@ export function handlePreferencesChangedEvent(msg: WebSocketMessage) {
|
||||
return async (dispatch: DispatchFunc, getState: GetStateFunc): Promise<ActionResult> => {
|
||||
const preferences: PreferenceType[] = JSON.parse(msg.data.preferences);
|
||||
const posts = getAllPosts(getState());
|
||||
const actions: Array<GenericAction> = [{
|
||||
const actions: GenericAction[] = [{
|
||||
type: PreferenceTypes.RECEIVED_PREFERENCES,
|
||||
data: preferences,
|
||||
}];
|
||||
|
||||
@@ -19,7 +19,7 @@ export function handleLeaveTeamEvent(msg: Partial<WebSocketMessage>) {
|
||||
const currentUser = getCurrentUser(state);
|
||||
|
||||
if (currentUser.id === msg.data.user_id) {
|
||||
const actions: Array<GenericAction> = [{type: TeamTypes.LEAVE_TEAM, data: teams[msg.data.team_id]}];
|
||||
const actions: GenericAction[] = [{type: TeamTypes.LEAVE_TEAM, data: teams[msg.data.team_id]}];
|
||||
if (isGuest(currentUser.roles)) {
|
||||
const notVisible = await notVisibleUsersActions(state);
|
||||
if (notVisible.length) {
|
||||
|
||||
@@ -31,7 +31,7 @@ export function handleUserAddedEvent(msg: WebSocketMessage) {
|
||||
const currentTeamId = getCurrentTeamId(state);
|
||||
const currentUserId = getCurrentUserId(state);
|
||||
const teamId = msg.data.team_id;
|
||||
const actions: Array<GenericAction> = [{
|
||||
const actions: GenericAction[] = [{
|
||||
type: ChannelTypes.CHANNEL_MEMBER_ADDED,
|
||||
data: {
|
||||
channel_id: msg.broadcast.channel_id,
|
||||
@@ -71,7 +71,7 @@ export function handleUserRemovedEvent(msg: WebSocketMessage) {
|
||||
const currentChannelId = getCurrentChannelId(state);
|
||||
const currentTeamId = getCurrentTeamId(state);
|
||||
const currentUser = getCurrentUser(state);
|
||||
const actions: Array<GenericAction> = [];
|
||||
const actions: GenericAction[] = [];
|
||||
let channelId;
|
||||
let userId;
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ const ClientApps = (superclass: any) => class extends superclass {
|
||||
`${this.getAppsProxyRoute()}/api/v1/call`,
|
||||
{method: 'post', body: JSON.stringify(callCopy)},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
getAppsBindings = async (userID: string, channelID: string, teamID: string) => {
|
||||
const params = {
|
||||
@@ -39,7 +39,7 @@ const ClientApps = (superclass: any) => class extends superclass {
|
||||
`${this.getAppsProxyRoute()}/api/v1/bindings${buildQueryString(params)}`,
|
||||
{method: 'get'},
|
||||
);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default ClientApps;
|
||||
|
||||
@@ -91,7 +91,7 @@ export default class ClientBase {
|
||||
|
||||
getWebSocketUrl = () => {
|
||||
return `${this.getBaseRoute()}/websocket`;
|
||||
}
|
||||
};
|
||||
|
||||
setAcceptLanguage(locale: string) {
|
||||
this.defaultHeaders['Accept-Language'] = locale;
|
||||
|
||||
@@ -19,21 +19,21 @@ const ClientBots = (superclass: any) => class extends superclass {
|
||||
`${this.getBotRoute(botUserId)}`,
|
||||
{method: 'get'},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
getBots = async (page = 0, perPage = PER_PAGE_DEFAULT) => {
|
||||
return this.doFetch(
|
||||
`${this.getBotsRoute()}${buildQueryString({page, per_page: perPage})}`,
|
||||
{method: 'get'},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
getBotsIncludeDeleted = async (page = 0, perPage = PER_PAGE_DEFAULT) => {
|
||||
return this.doFetch(
|
||||
`${this.getBotsRoute()}${buildQueryString({include_deleted: true, page, per_page: perPage})}`,
|
||||
{method: 'get'},
|
||||
);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default ClientBots;
|
||||
|
||||
@@ -41,7 +41,7 @@ const ClientFiles = (superclass: any) => class extends superclass {
|
||||
`${this.getFileRoute(fileId)}/link`,
|
||||
{method: 'get'},
|
||||
);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default ClientFiles;
|
||||
|
||||
@@ -27,7 +27,7 @@ const ClientGroups = (superclass: any) => class extends superclass {
|
||||
`${this.getUsersRoute()}/${userID}/groups`,
|
||||
{method: 'get'},
|
||||
);
|
||||
}
|
||||
};
|
||||
getAllGroupsAssociatedToTeam = async (teamID: string, filterAllowReference = false) => {
|
||||
return this.doFetch(
|
||||
`${this.getBaseRoute()}/teams/${teamID}/groups${buildQueryString({paginate: false, filter_allow_reference: filterAllowReference})}`,
|
||||
|
||||
@@ -202,7 +202,7 @@ const ClientPosts = (superclass: any) => class extends superclass {
|
||||
`${this.getUserRoute(userId)}/posts/${postId}/set_unread`,
|
||||
{method: 'post', body: JSON.stringify({collapsed_threads_supported: true})},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
pinPost = async (postId: string) => {
|
||||
analytics.trackAPI('api_posts_pin');
|
||||
|
||||
@@ -12,14 +12,14 @@ const ClientTos = (superclass: any) => class extends superclass {
|
||||
`${this.getUserRoute('me')}/terms_of_service`,
|
||||
{method: 'post', body: JSON.stringify({termsOfServiceId, accepted})},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
getTermsOfService = async () => {
|
||||
return this.doFetch(
|
||||
`${this.getBaseRoute()}/terms_of_service`,
|
||||
{method: 'get'},
|
||||
);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default ClientTos;
|
||||
|
||||
@@ -66,14 +66,14 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
||||
`${this.getUsersRoute()}${buildQueryString(queryParams)}`,
|
||||
{method: 'post', body: JSON.stringify(user)},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
patchMe = async (userPatch: Partial<UserProfile>) => {
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute('me')}/patch`,
|
||||
{method: 'put', body: JSON.stringify(userPatch)},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
patchUser = async (userPatch: Partial<UserProfile> & {id: string}) => {
|
||||
analytics.trackAPI('api_users_patch');
|
||||
@@ -82,7 +82,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
||||
`${this.getUserRoute(userPatch.id)}/patch`,
|
||||
{method: 'put', body: JSON.stringify(userPatch)},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
updateUser = async (user: UserProfile) => {
|
||||
analytics.trackAPI('api_users_update');
|
||||
@@ -91,7 +91,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
||||
`${this.getUserRoute(user.id)}`,
|
||||
{method: 'put', body: JSON.stringify(user)},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
demoteUserToGuest = async (userId: string) => {
|
||||
analytics.trackAPI('api_users_demote_user_to_guest');
|
||||
@@ -100,7 +100,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
||||
`${this.getUserRoute(userId)}/demote`,
|
||||
{method: 'post'},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
getKnownUsers = async () => {
|
||||
analytics.trackAPI('api_get_known_users');
|
||||
@@ -108,7 +108,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
||||
`${this.getUsersRoute()}/known`,
|
||||
{method: 'get'},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
sendPasswordResetEmail = async (email: string) => {
|
||||
analytics.trackAPI('api_users_send_password_reset');
|
||||
@@ -117,7 +117,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
||||
`${this.getUsersRoute()}/password/reset/send`,
|
||||
{method: 'post', body: JSON.stringify({email})},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
setDefaultProfileImage = async (userId: string) => {
|
||||
analytics.trackAPI('api_users_set_default_profile_picture');
|
||||
|
||||
@@ -137,7 +137,7 @@ export default class AtMention extends React.PureComponent {
|
||||
this.setState({
|
||||
user,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {isSearchResult, mentionName, mentionStyle, onPostPress, teammateNameDisplay, textStyle, mentionKeys, theme} = this.props;
|
||||
|
||||
@@ -146,7 +146,7 @@ export default class AttachmentButton extends PureComponent {
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
attachPhotoFromCamera = () => {
|
||||
return this.attachFileFromCamera('camera', 'photo');
|
||||
@@ -274,7 +274,7 @@ export default class AttachmentButton extends PureComponent {
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
};
|
||||
|
||||
hasPhotoPermission = async (source, mediaType = '') => {
|
||||
if (Platform.OS === 'ios') {
|
||||
|
||||
@@ -142,7 +142,7 @@ export default class Autocomplete extends PureComponent {
|
||||
|
||||
handleAppCommandCountChange = (appCommandCount) => {
|
||||
this.setState({appCommandCount});
|
||||
}
|
||||
};
|
||||
|
||||
handleCommandCountChange = (commandCount) => {
|
||||
this.setState({commandCount});
|
||||
|
||||
@@ -77,7 +77,7 @@ export default class DateSuggestion extends PureComponent {
|
||||
|
||||
setCalendarActive = (active) => {
|
||||
this.setState({active});
|
||||
}
|
||||
};
|
||||
|
||||
completeMention = (day) => {
|
||||
const mention = day.dateString;
|
||||
|
||||
@@ -140,7 +140,7 @@ export default class EmojiSuggestion extends PureComponent {
|
||||
});
|
||||
};
|
||||
|
||||
getItemLayout = ({index}) => ({length: 40, offset: 40 * index, index})
|
||||
getItemLayout = ({index}) => ({length: 40, offset: 40 * index, index});
|
||||
|
||||
keyExtractor = (item) => item;
|
||||
|
||||
@@ -174,7 +174,7 @@ export default class EmojiSuggestion extends PureComponent {
|
||||
});
|
||||
|
||||
this.props.onResultCountChange(0);
|
||||
}
|
||||
};
|
||||
|
||||
searchEmojis = (searchTerm) => {
|
||||
const {emojis} = this.props;
|
||||
|
||||
@@ -254,7 +254,7 @@ export class ParsedCommand {
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
// parseForm parses the rest of the command using the previously matched form.
|
||||
public parseForm = (autocompleteMode = false): ParsedCommand => {
|
||||
@@ -597,7 +597,7 @@ export class ParsedCommand {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export class AppCommandParser {
|
||||
@@ -649,7 +649,7 @@ export class AppCommandParser {
|
||||
}
|
||||
|
||||
return this.composeCallFromParsed(parsed);
|
||||
}
|
||||
};
|
||||
|
||||
private async addDefaultAndReadOnlyValues(parsed: ParsedCommand) {
|
||||
if (!parsed.form?.fields) {
|
||||
@@ -738,7 +738,7 @@ export class AppCommandParser {
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// getSuggestions returns suggestions for subcommands and/or form arguments
|
||||
public getSuggestions = async (pretext: string): Promise<ExtendedAutocompleteSuggestion[]> => {
|
||||
@@ -790,7 +790,7 @@ export class AppCommandParser {
|
||||
suggestions = this.getNoMatchingSuggestion();
|
||||
}
|
||||
return suggestions.map((suggestion) => this.decorateSuggestionComplete(parsed, suggestion));
|
||||
}
|
||||
};
|
||||
|
||||
getNoMatchingSuggestion = () => {
|
||||
return [{
|
||||
@@ -803,7 +803,7 @@ export class AppCommandParser {
|
||||
IconData: COMMAND_SUGGESTION_ERROR,
|
||||
Description: '',
|
||||
}];
|
||||
}
|
||||
};
|
||||
|
||||
getErrorSuggestion = (parsed: ParsedCommand) => {
|
||||
return [{
|
||||
@@ -816,7 +816,7 @@ export class AppCommandParser {
|
||||
IconData: COMMAND_SUGGESTION_ERROR,
|
||||
Description: parsed.error,
|
||||
}];
|
||||
}
|
||||
};
|
||||
|
||||
// composeCallFromParsed creates the form submission call
|
||||
private composeCallFromParsed = async (parsed: ParsedCommand): Promise<{call: AppCallRequest | null; errorMessage?: string}> => {
|
||||
@@ -846,7 +846,7 @@ export class AppCommandParser {
|
||||
|
||||
const context = this.getAppContext(parsed.binding);
|
||||
return {call: createCallRequest(call, context, {}, values, parsed.command)};
|
||||
}
|
||||
};
|
||||
|
||||
private expandOptions = async (parsed: ParsedCommand, values: AppCallValues): Promise<{errorMessage?: string}> => {
|
||||
if (!parsed.form?.fields) {
|
||||
@@ -935,7 +935,7 @@ export class AppCommandParser {
|
||||
errorMessage = errorMessage + errors[v] + '\n';
|
||||
});
|
||||
return {errorMessage};
|
||||
}
|
||||
};
|
||||
|
||||
// decorateSuggestionComplete applies the necessary modifications for a suggestion to be processed
|
||||
private decorateSuggestionComplete = (parsed: ParsedCommand, choice: AutocompleteSuggestion): AutocompleteSuggestion => {
|
||||
@@ -956,7 +956,7 @@ export class AppCommandParser {
|
||||
...choice,
|
||||
Complete: complete,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// getCommandBindings returns the commands in the redux store.
|
||||
// They are grouped by app id since each app has one base command
|
||||
@@ -966,19 +966,19 @@ export class AppCommandParser {
|
||||
return getRHSCommandBindings(state);
|
||||
}
|
||||
return getCommandBindings(state);
|
||||
}
|
||||
};
|
||||
|
||||
// getChannel gets the channel in which the user is typing the command
|
||||
private getChannel = (): Channel | null => {
|
||||
const state = this.store.getState();
|
||||
return selectChannel(state, this.channelID);
|
||||
}
|
||||
};
|
||||
|
||||
public setChannelContext = (channelID: string, teamID = '', rootPostID?: string) => {
|
||||
this.channelID = channelID;
|
||||
this.rootPostID = rootPostID;
|
||||
this.teamID = teamID;
|
||||
}
|
||||
};
|
||||
|
||||
// isAppCommand determines if subcommand/form suggestions need to be returned.
|
||||
// When this returns true, the caller knows that the parser should handle all suggestions for the current command string.
|
||||
@@ -1000,7 +1000,7 @@ export class AppCommandParser {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// getAppContext collects post/channel/team info for performing calls
|
||||
private getAppContext = (binding: AppBinding): AppContext => {
|
||||
@@ -1019,7 +1019,7 @@ export class AppCommandParser {
|
||||
context.team_id = channel.team_id || getCurrentTeamId(this.store.getState());
|
||||
|
||||
return context;
|
||||
}
|
||||
};
|
||||
|
||||
// fetchForm unconditionaly retrieves the form for the given binding (subcommand)
|
||||
private fetchForm = async (binding: AppBinding): Promise<{form?: AppForm; error?: string} | undefined> => {
|
||||
@@ -1066,7 +1066,7 @@ export class AppCommandParser {
|
||||
}
|
||||
|
||||
return {form: callResponse.form};
|
||||
}
|
||||
};
|
||||
|
||||
public getForm = async (location: string, binding: AppBinding): Promise<{form?: AppForm; error?: string} | undefined> => {
|
||||
const rootID = this.rootPostID || '';
|
||||
@@ -1088,7 +1088,7 @@ export class AppCommandParser {
|
||||
});
|
||||
}
|
||||
return fetched;
|
||||
}
|
||||
};
|
||||
|
||||
// getSuggestionsForSubCommands returns suggestions for a subcommand's name
|
||||
private getCommandSuggestions = (parsed: ParsedCommand): AutocompleteSuggestion[] => {
|
||||
@@ -1111,7 +1111,7 @@ export class AppCommandParser {
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// getParameterSuggestions computes suggestions for positional argument values, flag names, and flag argument values
|
||||
private getParameterSuggestions = async (parsed: ParsedCommand): Promise<ExtendedAutocompleteSuggestion[]> => {
|
||||
@@ -1149,7 +1149,7 @@ export class AppCommandParser {
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
// getMissingFields collects the required fields that were not supplied in a submission
|
||||
private getMissingFields = (parsed: ParsedCommand): AppField[] => {
|
||||
@@ -1169,7 +1169,7 @@ export class AppCommandParser {
|
||||
}
|
||||
|
||||
return missing;
|
||||
}
|
||||
};
|
||||
|
||||
// getFlagNameSuggestions returns suggestions for flag names
|
||||
private getFlagNameSuggestions = (parsed: ParsedCommand): AutocompleteSuggestion[] => {
|
||||
@@ -1201,7 +1201,7 @@ export class AppCommandParser {
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
// getSuggestionsForField gets suggestions for a positional or flag field value
|
||||
private getValueSuggestions = async (parsed: ParsedCommand, delimiter?: string): Promise<ExtendedAutocompleteSuggestion[]> => {
|
||||
@@ -1242,7 +1242,7 @@ export class AppCommandParser {
|
||||
Hint: '',
|
||||
IconData: parsed.binding?.icon || '',
|
||||
}];
|
||||
}
|
||||
};
|
||||
|
||||
// getStaticSelectSuggestions returns suggestions specified in the field's options property
|
||||
private getStaticSelectSuggestions = (parsed: ParsedCommand, delimiter?: string): AutocompleteSuggestion[] => {
|
||||
@@ -1275,7 +1275,7 @@ export class AppCommandParser {
|
||||
IconData: opt.icon_data || parsed.binding?.icon || '',
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// getDynamicSelectSuggestions fetches and returns suggestions from the server
|
||||
private getDynamicSelectSuggestions = async (parsed: ParsedCommand, delimiter?: string): Promise<AutocompleteSuggestion[]> => {
|
||||
@@ -1364,7 +1364,7 @@ export class AppCommandParser {
|
||||
IconData: s.icon_data || parsed.binding?.icon || '',
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
private makeDynamicSelectSuggestionError = (message: string): AutocompleteSuggestion[] => {
|
||||
const errMsg = this.intl.formatMessage({
|
||||
@@ -1383,7 +1383,7 @@ export class AppCommandParser {
|
||||
IconData: COMMAND_SUGGESTION_ERROR,
|
||||
Description: errMsg,
|
||||
}];
|
||||
}
|
||||
};
|
||||
|
||||
private getUserFieldSuggestions = async (parsed: ParsedCommand): Promise<AutocompleteSuggestion[]> => {
|
||||
let input = parsed.incomplete.trim();
|
||||
@@ -1392,7 +1392,7 @@ export class AppCommandParser {
|
||||
}
|
||||
const {data} = await this.store.dispatch(autocompleteUsersInChannel(input, this.channelID));
|
||||
return getUserSuggestions(data);
|
||||
}
|
||||
};
|
||||
|
||||
private getChannelFieldSuggestions = async (parsed: ParsedCommand): Promise<AutocompleteSuggestion[]> => {
|
||||
let input = parsed.incomplete.trim();
|
||||
@@ -1401,7 +1401,7 @@ export class AppCommandParser {
|
||||
}
|
||||
const {data} = await this.store.dispatch(autocompleteChannels(this.teamID, input));
|
||||
return getChannelSuggestions(data);
|
||||
}
|
||||
};
|
||||
|
||||
// getBooleanSuggestions returns true/false suggestions
|
||||
private getBooleanSuggestions = (parsed: ParsedCommand): AutocompleteSuggestion[] => {
|
||||
@@ -1426,7 +1426,7 @@ export class AppCommandParser {
|
||||
});
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function isMultiword(value: string) {
|
||||
|
||||
@@ -109,13 +109,13 @@ export default class AppSlashSuggestion extends PureComponent<Props, State> {
|
||||
isAppCommand = (pretext: string, channelID: string, teamID = '', rootID?: string) => {
|
||||
this.appCommandParser.setChannelContext(channelID, teamID, rootID);
|
||||
return this.appCommandParser.isAppCommand(pretext);
|
||||
}
|
||||
};
|
||||
|
||||
fetchAndShowAppCommandSuggestions = async (pretext: string, channelID: string, teamID = '', rootID?: string) => {
|
||||
this.appCommandParser.setChannelContext(channelID, teamID, rootID);
|
||||
const suggestions = await this.appCommandParser.getSuggestions(pretext);
|
||||
this.updateSuggestions(suggestions);
|
||||
}
|
||||
};
|
||||
|
||||
updateSuggestions = (matches: ExtendedAutocompleteSuggestion[]) => {
|
||||
this.setState({
|
||||
@@ -123,7 +123,7 @@ export default class AppSlashSuggestion extends PureComponent<Props, State> {
|
||||
dataSource: matches,
|
||||
});
|
||||
this.props.onResultCountChange(matches.length);
|
||||
}
|
||||
};
|
||||
|
||||
completeSuggestion = (command: string) => {
|
||||
const {onChangeText} = this.props;
|
||||
@@ -157,13 +157,13 @@ export default class AppSlashSuggestion extends PureComponent<Props, State> {
|
||||
return () => {
|
||||
this.completeSuggestion(base);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
completeChannelMention = (base: string): (channelName: string) => void => {
|
||||
return () => {
|
||||
this.completeSuggestion(base);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
keyExtractor = (item: ExtendedAutocompleteSuggestion): string => item.Suggestion + item.type + item.item;
|
||||
|
||||
@@ -198,7 +198,7 @@ export default class AppSlashSuggestion extends PureComponent<Props, State> {
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {maxListHeight, theme, nestedScrollEnabled} = this.props;
|
||||
|
||||
@@ -144,13 +144,13 @@ export default class SlashSuggestion extends PureComponent<Props, State> {
|
||||
});
|
||||
|
||||
this.updateSuggestions(matches);
|
||||
}
|
||||
};
|
||||
|
||||
getAppBaseCommandSuggestions = (pretext: string, channelID: string, teamID = '', rootID?: string): AutocompleteSuggestion[] => {
|
||||
this.appCommandParser.setChannelContext(channelID, teamID, rootID);
|
||||
const suggestions = this.appCommandParser.getSuggestionsBase(pretext);
|
||||
return suggestions;
|
||||
}
|
||||
};
|
||||
|
||||
updateSuggestions = (matches: AutocompleteSuggestion[]) => {
|
||||
this.setState({
|
||||
@@ -158,7 +158,7 @@ export default class SlashSuggestion extends PureComponent<Props, State> {
|
||||
dataSource: matches,
|
||||
});
|
||||
this.props.onResultCountChange(matches.length);
|
||||
}
|
||||
};
|
||||
|
||||
filterCommands = (matchTerm: string, commands: Command[]): AutocompleteSuggestion[] => {
|
||||
const data = commands.filter((command) => {
|
||||
@@ -179,11 +179,11 @@ export default class SlashSuggestion extends PureComponent<Props, State> {
|
||||
IconData: item.icon_url || item.autocomplete_icon_data || '',
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
contains = (matches: AutocompleteSuggestion[], complete: string): boolean => {
|
||||
return matches.findIndex((match) => match.Complete === complete) !== -1;
|
||||
}
|
||||
};
|
||||
|
||||
completeSuggestion = (command: string) => {
|
||||
const {onChangeText} = this.props;
|
||||
@@ -219,7 +219,7 @@ export default class SlashSuggestion extends PureComponent<Props, State> {
|
||||
complete={item.Complete}
|
||||
icon={item.IconData}
|
||||
/>
|
||||
)
|
||||
);
|
||||
|
||||
render() {
|
||||
const {maxListHeight, theme, nestedScrollEnabled} = this.props;
|
||||
|
||||
@@ -83,7 +83,7 @@ export default class ChannelLink extends React.PureComponent {
|
||||
this.props.onChannelLinkPress(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const channel = this.state.channel;
|
||||
|
||||
@@ -92,7 +92,7 @@ export default class ChannelLoader extends PureComponent {
|
||||
delay: 500,
|
||||
useNativeDriver: false,
|
||||
}).start();
|
||||
}
|
||||
};
|
||||
|
||||
buildSections({key, style, bg, color}) {
|
||||
return (
|
||||
@@ -128,7 +128,7 @@ export default class ChannelLoader extends PureComponent {
|
||||
const {height} = e.nativeEvent.layout;
|
||||
const maxRows = calculateMaxRows(height);
|
||||
this.setState({maxRows});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
|
||||
@@ -163,7 +163,7 @@ export default class EditChannelInfo extends PureComponent {
|
||||
|
||||
onHeaderLayout = ({nativeEvent}) => {
|
||||
this.setState({headerPosition: nativeEvent.layout.y});
|
||||
}
|
||||
};
|
||||
|
||||
onKeyboardDidShow = () => {
|
||||
this.setState({keyboardVisible: true});
|
||||
@@ -172,15 +172,15 @@ export default class EditChannelInfo extends PureComponent {
|
||||
this.setState({headerHasFocus: false});
|
||||
this.scrollHeaderToTop();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onKeyboardDidHide = () => {
|
||||
this.setState({keyboardVisible: false});
|
||||
}
|
||||
};
|
||||
|
||||
onKeyboardOffsetChanged = (keyboardPosition) => {
|
||||
this.setState({keyboardPosition});
|
||||
}
|
||||
};
|
||||
|
||||
onHeaderFocus = () => {
|
||||
if (this.state.keyboardVisible) {
|
||||
@@ -194,7 +194,7 @@ export default class EditChannelInfo extends PureComponent {
|
||||
if (this.scroll.current) {
|
||||
this.scroll.current.scrollTo({x: 0, y: this.state.headerPosition});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
|
||||
@@ -109,7 +109,7 @@ export default class EmojiPicker extends PureComponent {
|
||||
|
||||
setSearchBarRef = (ref) => {
|
||||
this.searchBarRef = ref;
|
||||
}
|
||||
};
|
||||
|
||||
setSectionListRef = (ref) => {
|
||||
this.sectionListRef = ref;
|
||||
@@ -121,7 +121,7 @@ export default class EmojiPicker extends PureComponent {
|
||||
const emojis = this.renderableEmojis(this.props.emojisBySection, this.props.deviceWidth);
|
||||
this.setState({emojis});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
renderableEmojis = (emojis, deviceWidth) => {
|
||||
const numberOfColumns = this.getNumberOfColumns(deviceWidth);
|
||||
@@ -501,7 +501,7 @@ export default class EmojiPicker extends PureComponent {
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const getStyleSheetFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
|
||||
@@ -18,7 +18,7 @@ export default class EmojiPickerRow extends Component {
|
||||
emojiSize: PropTypes.number.isRequired,
|
||||
items: PropTypes.array.isRequired,
|
||||
onEmojiPress: PropTypes.func.isRequired,
|
||||
}
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return !shallowEqual(this.props.items, nextProps.items);
|
||||
|
||||
@@ -62,7 +62,7 @@ export default class ErrorList extends PureComponent {
|
||||
clearErrors: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
errors: PropTypes.array.isRequired,
|
||||
}
|
||||
};
|
||||
|
||||
renderErrorsList() {
|
||||
const {errors} = this.props;
|
||||
|
||||
@@ -54,7 +54,7 @@ class FormattedMarkdownText extends React.PureComponent {
|
||||
|
||||
createParser = () => {
|
||||
return new Parser();
|
||||
}
|
||||
};
|
||||
|
||||
createRenderer = () => {
|
||||
return new Renderer({
|
||||
@@ -73,39 +73,39 @@ class FormattedMarkdownText extends React.PureComponent {
|
||||
atMention: this.renderAtMention,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
computeTextStyle = (baseStyle, context) => {
|
||||
return concatStyles(baseStyle, context.map((type) => this.textStyles[type]));
|
||||
}
|
||||
};
|
||||
|
||||
renderText = ({context, literal}) => {
|
||||
const style = this.computeTextStyle(this.props.style || this.baseTextStyle, context);
|
||||
return <Text style={style}>{literal}</Text>;
|
||||
}
|
||||
};
|
||||
|
||||
renderCodeSpan = ({context, literal}) => {
|
||||
const style = this.computeTextStyle([this.baseTextStyle, this.textStyles.code], context);
|
||||
return <Text style={style}>{literal}</Text>;
|
||||
}
|
||||
};
|
||||
|
||||
renderLink = ({children, href}) => {
|
||||
const url = href[0] === TARGET_BLANK_URL_PREFIX ? href.substring(1, href.length) : href;
|
||||
return <MarkdownLink href={url}>{children}</MarkdownLink>;
|
||||
}
|
||||
};
|
||||
|
||||
renderBreak = () => {
|
||||
return <Text>{'\n'}</Text>;
|
||||
}
|
||||
};
|
||||
|
||||
renderParagraph = ({children}) => {
|
||||
return <Text>{children}</Text>;
|
||||
}
|
||||
};
|
||||
|
||||
renderHTML = (props) => {
|
||||
console.warn(`HTML used in FormattedMarkdownText component with id ${this.props.id}`); // eslint-disable-line no-console
|
||||
return this.renderText(props);
|
||||
}
|
||||
};
|
||||
|
||||
renderAtMention = ({context, mentionName}) => {
|
||||
const style = getStyleSheet(this.props.theme);
|
||||
@@ -118,7 +118,7 @@ class FormattedMarkdownText extends React.PureComponent {
|
||||
textStyle={[this.computeTextStyle(this.props.baseTextStyle, context), style.atMentionOpacity]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {id, defaultMessage, values, theme} = this.props;
|
||||
|
||||
@@ -28,12 +28,12 @@ type Props = {
|
||||
markAllThreadsInTeamRead: (userId: $ID<UserProfile>, teamId: $ID<Team>) => void;
|
||||
selectPost: (postId: string) => void;
|
||||
};
|
||||
allThreadIds: $ID<UserThread>[];
|
||||
allThreadIds: Array<$ID<UserThread>>;
|
||||
intl: typeof intlShape;
|
||||
teamId: $ID<Team>;
|
||||
theme: Theme;
|
||||
threadCount: ThreadsState['counts'][$ID<Team>];
|
||||
unreadThreadIds: $ID<UserThread>[];
|
||||
unreadThreadIds: Array<$ID<UserThread>>;
|
||||
userId: $ID<UserProfile>;
|
||||
viewingUnreads: boolean;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export type Props = {
|
||||
onRefresh: () => void;
|
||||
testID: string;
|
||||
theme: Theme;
|
||||
threadIds: $ID<UserThread>[];
|
||||
threadIds: Array<$ID<UserThread>>;
|
||||
viewAllThreads: () => void;
|
||||
viewUnreadThreads: () => void;
|
||||
viewingUnreads: boolean;
|
||||
|
||||
@@ -92,13 +92,13 @@ export default class InteractiveDialogController extends PureComponent {
|
||||
};
|
||||
|
||||
showModal('InteractiveDialog', dialog.title, null, options);
|
||||
}
|
||||
};
|
||||
|
||||
handleCancel = (dialog, url) => {
|
||||
if (dialog.notify_on_cancel) {
|
||||
this.props.actions.submitInteractiveDialog({...dialog, url, cancelled: true});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return null;
|
||||
|
||||
@@ -29,7 +29,7 @@ export default class MarkdownEmoji extends PureComponent {
|
||||
|
||||
createParser = () => {
|
||||
return new Parser();
|
||||
}
|
||||
};
|
||||
|
||||
createRenderer = () => {
|
||||
return new Renderer({
|
||||
@@ -80,7 +80,7 @@ export default class MarkdownEmoji extends PureComponent {
|
||||
renderNewLine = ({context}) => {
|
||||
const style = this.computeTextStyle(this.props.baseTextStyle, context);
|
||||
return <Text style={style}>{'\n'}</Text>;
|
||||
}
|
||||
};
|
||||
|
||||
renderEditedIndicator = ({context}) => {
|
||||
let spacer = '';
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getCurrentUrl} from '@mm-redux/selectors/entities/general';
|
||||
import {getTheme} from '@mm-redux/selectors/entities/preferences';
|
||||
|
||||
import MarkdownImage from './markdown_image';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
serverURL: getCurrentUrl(state),
|
||||
theme: getTheme(state),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ export default class MarkdownImage extends ImageViewPort {
|
||||
linkDestination: PropTypes.string,
|
||||
postId: PropTypes.string,
|
||||
source: PropTypes.string.isRequired,
|
||||
theme: PropTypes.object,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
@@ -185,6 +186,7 @@ export default class MarkdownImage extends ImageViewPort {
|
||||
<CompassIcon
|
||||
name='file-image-broken-outline-large'
|
||||
size={24}
|
||||
color={this.props.theme?.centerChannelColor}
|
||||
/>
|
||||
);
|
||||
} else if (width && height) {
|
||||
@@ -228,6 +230,7 @@ export default class MarkdownImage extends ImageViewPort {
|
||||
<SvgUri
|
||||
uri={fileInfo.uri}
|
||||
style={{flex: 1}}
|
||||
onError={this.handleSizeFailed}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ export default class MarkdownTable extends React.PureComponent {
|
||||
setMaxPreviewColumns = ({window}) => {
|
||||
const maxPreviewColumns = Math.floor(window.width / CELL_MIN_WIDTH);
|
||||
this.setState({maxPreviewColumns});
|
||||
}
|
||||
};
|
||||
|
||||
getTableWidth = (isFullView = false) => {
|
||||
const maxPreviewColumns = this.state.maxPreviewColumns || MAX_PREVIEW_COLUMNS;
|
||||
@@ -97,7 +97,7 @@ export default class MarkdownTable extends React.PureComponent {
|
||||
|
||||
renderPreviewRows = (isFullView = false) => {
|
||||
return this.renderRows(isFullView, true);
|
||||
}
|
||||
};
|
||||
|
||||
shouldRenderAsFlex = (isFullView = false) => {
|
||||
const {numColumns} = this.props;
|
||||
@@ -123,7 +123,7 @@ export default class MarkdownTable extends React.PureComponent {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
getTableStyle = (isFullView) => {
|
||||
const {theme} = this.props;
|
||||
@@ -138,7 +138,7 @@ export default class MarkdownTable extends React.PureComponent {
|
||||
|
||||
tableStyle.push({width: this.getTableWidth(isFullView)});
|
||||
return tableStyle;
|
||||
}
|
||||
};
|
||||
|
||||
renderRows = (isFullView = false, isPreview = false) => {
|
||||
const tableStyle = this.getTableStyle(isFullView);
|
||||
@@ -180,7 +180,7 @@ export default class MarkdownTable extends React.PureComponent {
|
||||
{rows}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {containerWidth, contentHeight} = this.state;
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getCurrentUrl} from '@mm-redux/selectors/entities/general';
|
||||
import {getTheme} from '@mm-redux/selectors/entities/preferences';
|
||||
|
||||
import MarkdownTableImage from './markdown_table_image';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
serverURL: getCurrentUrl(state),
|
||||
theme: getTheme(state),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import {openGalleryAtIndex} from '@utils/gallery';
|
||||
import {calculateDimensions, isGifTooLarge} from '@utils/images';
|
||||
|
||||
import type {PostImage} from '@mm-redux/types/posts';
|
||||
import type {Theme} from '@mm-redux/types/theme';
|
||||
|
||||
type MarkdownTableImageProps = {
|
||||
disable: boolean;
|
||||
@@ -23,6 +24,7 @@ type MarkdownTableImageProps = {
|
||||
postId: string;
|
||||
serverURL?: string;
|
||||
source: string;
|
||||
theme: Theme;
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
@@ -32,7 +34,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
const MarkTableImage = ({disable, imagesMetadata, postId, serverURL, source}: MarkdownTableImageProps) => {
|
||||
const MarkTableImage = ({disable, imagesMetadata, postId, serverURL, source, theme}: MarkdownTableImageProps) => {
|
||||
const metadata = imagesMetadata[source] || Object.values(imagesMetadata || {})?.[0];
|
||||
const fileId = useRef(generateId()).current;
|
||||
const [failed, setFailed] = useState(isGifTooLarge(metadata));
|
||||
@@ -98,6 +100,7 @@ const MarkTableImage = ({disable, imagesMetadata, postId, serverURL, source}: Ma
|
||||
<CompassIcon
|
||||
name='file-image-broken-outline-large'
|
||||
size={24}
|
||||
color={theme.centerChannelColor}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
@@ -109,6 +112,9 @@ const MarkTableImage = ({disable, imagesMetadata, postId, serverURL, source}: Ma
|
||||
<SvgUri
|
||||
uri={source}
|
||||
style={styles.container}
|
||||
|
||||
//@ts-expect-error onError not defined in the types
|
||||
onError={onLoadFailed}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -46,14 +46,7 @@ const styles = StyleSheet.create({
|
||||
height: ViewTypes.INDICATOR_BAR_HEIGHT,
|
||||
width: '100%',
|
||||
position: 'absolute',
|
||||
...Platform.select({
|
||||
android: {
|
||||
elevation: 9,
|
||||
},
|
||||
ios: {
|
||||
zIndex: 9,
|
||||
},
|
||||
}),
|
||||
zIndex: 9,
|
||||
},
|
||||
wrapper: {
|
||||
alignItems: 'center',
|
||||
|
||||
@@ -70,7 +70,6 @@ exports[`PostDraft Should render the Archived for channelIsArchived 1`] = `
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
focusable={true}
|
||||
nativeID="animatedComponent"
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
@@ -176,7 +175,6 @@ exports[`PostDraft Should render the Archived for deactivatedChannel 1`] = `
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
focusable={true}
|
||||
nativeID="animatedComponent"
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
@@ -218,7 +216,6 @@ exports[`PostDraft Should render the DraftInput 1`] = `
|
||||
>
|
||||
<View
|
||||
collapsable={false}
|
||||
nativeID="animatedComponent"
|
||||
style={
|
||||
Object {
|
||||
"bottom": 0,
|
||||
@@ -347,7 +344,6 @@ exports[`PostDraft Should render the DraftInput 1`] = `
|
||||
collapsable={false}
|
||||
forwardedRef={[Function]}
|
||||
isInteraction={true}
|
||||
nativeID="animatedComponent"
|
||||
style={
|
||||
Object {
|
||||
"display": "flex",
|
||||
@@ -379,7 +375,6 @@ exports[`PostDraft Should render the DraftInput 1`] = `
|
||||
collapsable={false}
|
||||
forwardedRef={[Function]}
|
||||
isInteraction={true}
|
||||
nativeID="animatedComponent"
|
||||
style={
|
||||
Object {
|
||||
"height": 0,
|
||||
@@ -427,7 +422,6 @@ exports[`PostDraft Should render the DraftInput 1`] = `
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
focusable={true}
|
||||
nativeID="animatedComponent"
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
@@ -460,7 +454,6 @@ exports[`PostDraft Should render the DraftInput 1`] = `
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
focusable={true}
|
||||
nativeID="animatedComponent"
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
@@ -493,7 +486,6 @@ exports[`PostDraft Should render the DraftInput 1`] = `
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
focusable={true}
|
||||
nativeID="animatedComponent"
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
@@ -526,7 +518,6 @@ exports[`PostDraft Should render the DraftInput 1`] = `
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
focusable={true}
|
||||
nativeID="animatedComponent"
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
@@ -559,7 +550,6 @@ exports[`PostDraft Should render the DraftInput 1`] = `
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
focusable={true}
|
||||
nativeID="animatedComponent"
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
|
||||
@@ -20,7 +20,7 @@ export default class Archived extends PureComponent {
|
||||
selectPenultimateChannel: PropTypes.func.isRequired,
|
||||
teamId: PropTypes.string.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
}
|
||||
};
|
||||
|
||||
onCloseChannelPress = () => {
|
||||
const {rootId, teamId} = this.props;
|
||||
|
||||
@@ -208,7 +208,7 @@ export default class DraftInput extends PureComponent {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleInputQuickAction = (inputValue) => {
|
||||
if (this.input.current) {
|
||||
@@ -363,7 +363,7 @@ export default class DraftInput extends PureComponent {
|
||||
this.input.current.setValue(value, autocomplete);
|
||||
this.updateCanSubmit();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
showSendToAllOrChannelOrHereAlert = (membersCount, msg, atHere) => {
|
||||
const {formatMessage} = this.context.intl;
|
||||
@@ -396,7 +396,7 @@ export default class DraftInput extends PureComponent {
|
||||
if (canSubmit !== enabled) {
|
||||
this.setState({canSubmit: enabled});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
updateQuickActionValue = (value) => {
|
||||
if (this.quickActions.current) {
|
||||
@@ -404,7 +404,7 @@ export default class DraftInput extends PureComponent {
|
||||
}
|
||||
|
||||
this.updateCanSubmit();
|
||||
}
|
||||
};
|
||||
|
||||
updateStatus = (status) => {
|
||||
const {currentUserId, setStatus} = this.props;
|
||||
|
||||
@@ -44,7 +44,7 @@ export default class PostInput extends PureComponent {
|
||||
theme: PropTypes.object.isRequired,
|
||||
updateInitialValue: PropTypes.func.isRequired,
|
||||
userTyping: PropTypes.func.isRequired,
|
||||
}
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -139,7 +139,7 @@ export default class PostInput extends PureComponent {
|
||||
if (this.input.current) {
|
||||
this.input.current.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
getPlaceHolder = () => {
|
||||
const {rootId} = this.props;
|
||||
@@ -261,11 +261,11 @@ export default class PostInput extends PureComponent {
|
||||
|
||||
onManagedConfigurationChange = (config) => {
|
||||
this.setState({disableCopyAndPaste: config.copyAndPasteProtection === 'true'});
|
||||
}
|
||||
};
|
||||
|
||||
onPaste = (error, files) => {
|
||||
EventEmitter.emit(PASTE_FILES, error, files, this.props.screenId);
|
||||
}
|
||||
};
|
||||
|
||||
resetTextInput = () => {
|
||||
if (this.input.current) {
|
||||
@@ -273,7 +273,7 @@ export default class PostInput extends PureComponent {
|
||||
text: '',
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setValue = (value, autocomplete = false) => {
|
||||
this.value = value;
|
||||
@@ -283,7 +283,7 @@ export default class PostInput extends PureComponent {
|
||||
});
|
||||
this.handleTextChange(value, autocomplete);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onPressIn = () => {
|
||||
if (Platform.OS === 'ios') {
|
||||
|
||||
@@ -10,7 +10,6 @@ exports[`CameraButton should match snapshot 1`] = `
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
focusable={true}
|
||||
nativeID="animatedComponent"
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
|
||||
@@ -10,7 +10,6 @@ exports[`FileQuickAction should match snapshot 1`] = `
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
focusable={true}
|
||||
nativeID="animatedComponent"
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
|
||||
@@ -10,7 +10,6 @@ exports[`ImageQuickAction should match snapshot 1`] = `
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
focusable={true}
|
||||
nativeID="animatedComponent"
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
|
||||
@@ -38,7 +38,7 @@ export default class InputQuickAction extends PureComponent {
|
||||
}
|
||||
|
||||
onTextChange(newValue);
|
||||
}
|
||||
};
|
||||
|
||||
renderInput = () => {
|
||||
const {disabled, inputType, theme} = this.props;
|
||||
@@ -54,7 +54,7 @@ export default class InputQuickAction extends PureComponent {
|
||||
size={ICON_SIZE}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {testID, disabled, theme} = this.props;
|
||||
|
||||
@@ -59,11 +59,11 @@ export default class QuickActions extends PureComponent {
|
||||
handleOnTextChange = (newValue) => {
|
||||
this.handleInputEvent(newValue);
|
||||
this.props.onTextChange(newValue);
|
||||
}
|
||||
};
|
||||
|
||||
handleUploadFiles = (files) => {
|
||||
EventEmitter.emit(UPLOAD_FILES, files, this.props.screenId);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
|
||||
@@ -52,7 +52,7 @@ export default class Typing extends PureComponent {
|
||||
duration,
|
||||
useNativeDriver: false,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderTyping = () => {
|
||||
const {typing} = this.props;
|
||||
|
||||
@@ -56,7 +56,7 @@ export default class UploadItem extends PureComponent {
|
||||
|
||||
handlePress = () => {
|
||||
this.props.onPress(this.props.file);
|
||||
}
|
||||
};
|
||||
|
||||
handleRetryFileUpload = (file) => {
|
||||
if (!file.failed) {
|
||||
@@ -120,7 +120,7 @@ export default class UploadItem extends PureComponent {
|
||||
}
|
||||
|
||||
this.uploadFile(newFile);
|
||||
}
|
||||
};
|
||||
|
||||
uploadFile = async (file) => {
|
||||
const {channelId} = this.props;
|
||||
|
||||
@@ -118,7 +118,7 @@ export default class Uploads extends PureComponent {
|
||||
const {files} = this.props;
|
||||
const index = files.indexOf(file);
|
||||
openGalleryAtIndex(index, files.filter((f) => !f.failed && !f.loading));
|
||||
}
|
||||
};
|
||||
|
||||
clearErrorsFromState = () => {
|
||||
this.setState({
|
||||
@@ -126,7 +126,7 @@ export default class Uploads extends PureComponent {
|
||||
showFileMaxWarning: false,
|
||||
fileSizeWarning: null,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleAndroidBack = () => {
|
||||
const {channelId, files, handleRemoveLastFile, rootId} = this.props;
|
||||
@@ -255,7 +255,7 @@ export default class Uploads extends PureComponent {
|
||||
|
||||
this.makeErrorVisible(true);
|
||||
this.hideErrorTimer = setTimeout(this.hideError, 5000);
|
||||
}
|
||||
};
|
||||
|
||||
hideError = () => this.makeErrorVisible(false);
|
||||
|
||||
@@ -270,7 +270,7 @@ export default class Uploads extends PureComponent {
|
||||
this.clearErrorsFromState();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
showOrHideContainer = () => {
|
||||
const {
|
||||
@@ -286,7 +286,7 @@ export default class Uploads extends PureComponent {
|
||||
this.containerRef.current.transition(hideFiles, showFiles, 350, 'ease-in');
|
||||
this.shown = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
showPasteFilesErrorDialog = () => {
|
||||
const {formatMessage} = this.context.intl;
|
||||
|
||||
@@ -51,7 +51,7 @@ type MoreMessagesButtonProps = {
|
||||
newMessageLineIndex: number;
|
||||
postIds: string[];
|
||||
registerScrollEndIndexListener: (fn: (endIndex: number) => void) => () => void;
|
||||
registerViewableItemsListener: (fn: (viewableItems: ViewToken[]) => void) => () =>void;
|
||||
registerViewableItemsListener: (fn: (viewableItems: ViewToken[]) => void) => () => void;
|
||||
resetUnreadMessageCount: (channelId: string) => void;
|
||||
scrollToIndex: (index: number, animated?: boolean) => void;
|
||||
theme: Theme;
|
||||
@@ -146,22 +146,22 @@ export default class MoreMessageButton extends React.PureComponent<MoreMessagesB
|
||||
if (!isActive && channelId) {
|
||||
this.props.resetUnreadMessageCount(channelId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onIndicatorBarVisible = (indicatorVisible: boolean) => {
|
||||
this.indicatorBarVisible = indicatorVisible;
|
||||
this.animateButton();
|
||||
}
|
||||
};
|
||||
|
||||
onCurrentCallBarVisible = (currentCallVisible: boolean) => {
|
||||
this.currentCallBarVisible = currentCallVisible;
|
||||
this.animateButton();
|
||||
}
|
||||
};
|
||||
|
||||
onJoinCallBarVisible = (joinCallVisible: boolean) => {
|
||||
this.joinCallBarVisible = joinCallVisible;
|
||||
this.animateButton();
|
||||
}
|
||||
};
|
||||
|
||||
animateButton = () => {
|
||||
if (this.buttonVisible) {
|
||||
@@ -171,7 +171,7 @@ export default class MoreMessageButton extends React.PureComponent<MoreMessagesB
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
reset = () => {
|
||||
if (this.autoCancelTimer) {
|
||||
@@ -184,14 +184,14 @@ export default class MoreMessageButton extends React.PureComponent<MoreMessagesB
|
||||
this.pressed = false;
|
||||
this.canceled = false;
|
||||
this.disableViewableItems = false;
|
||||
}
|
||||
};
|
||||
|
||||
show = () => {
|
||||
if (!this.buttonVisible && this.state.moreText && !this.props.deepLinkURL && !this.canceled && this.props.unreadCount > 0) {
|
||||
this.buttonVisible = true;
|
||||
this.animateButton();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
getBarsFactor = () => {
|
||||
return Math.abs((
|
||||
@@ -199,7 +199,7 @@ export default class MoreMessageButton extends React.PureComponent<MoreMessagesB
|
||||
(this.joinCallBarVisible ? 0 : JOIN_CALL_BAR_HEIGHT) +
|
||||
(this.currentCallBarVisible ? 0 : CURRENT_CALL_BAR_HEIGHT)
|
||||
) / (HIDDEN_TOP - SHOWN_TOP));
|
||||
}
|
||||
};
|
||||
|
||||
hide = () => {
|
||||
if (this.buttonVisible) {
|
||||
@@ -210,7 +210,7 @@ export default class MoreMessageButton extends React.PureComponent<MoreMessagesB
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
cancel = (force = false) => {
|
||||
if (!force && (this.indicatorBarVisible || this.props.loadingPosts)) {
|
||||
@@ -228,7 +228,7 @@ export default class MoreMessageButton extends React.PureComponent<MoreMessagesB
|
||||
this.canceled = true;
|
||||
this.disableViewableItems = true;
|
||||
this.hide();
|
||||
}
|
||||
};
|
||||
|
||||
uncancel = () => {
|
||||
if (this.autoCancelTimer) {
|
||||
@@ -237,7 +237,7 @@ export default class MoreMessageButton extends React.PureComponent<MoreMessagesB
|
||||
}
|
||||
this.canceled = false;
|
||||
this.disableViewableItems = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMoreMessagesPress = () => {
|
||||
if (this.pressed) {
|
||||
@@ -249,7 +249,7 @@ export default class MoreMessageButton extends React.PureComponent<MoreMessagesB
|
||||
const {newMessageLineIndex, scrollToIndex} = this.props;
|
||||
this.pressed = true;
|
||||
scrollToIndex(newMessageLineIndex, true);
|
||||
}
|
||||
};
|
||||
|
||||
onViewableItemsChanged = (viewableItems: ViewToken[]) => {
|
||||
this.viewableItems = viewableItems;
|
||||
@@ -295,7 +295,7 @@ export default class MoreMessageButton extends React.PureComponent<MoreMessagesB
|
||||
readCount = 0;
|
||||
this.showMoreText(readCount);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
showMoreText = (readCount: number) => {
|
||||
const moreCount = this.props.unreadCount - readCount;
|
||||
@@ -304,7 +304,7 @@ export default class MoreMessageButton extends React.PureComponent<MoreMessagesB
|
||||
const moreText = this.moreText(moreCount);
|
||||
this.setState({moreText}, this.show);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
getReadCount = (lastViewableIndex: number) => {
|
||||
const {postIds} = this.props;
|
||||
@@ -313,7 +313,7 @@ export default class MoreMessageButton extends React.PureComponent<MoreMessagesB
|
||||
const readCount = messageCount(viewedPostIds);
|
||||
|
||||
return readCount;
|
||||
}
|
||||
};
|
||||
|
||||
moreText = (count: number) => {
|
||||
const {unreadCount} = this.props;
|
||||
@@ -324,12 +324,12 @@ export default class MoreMessageButton extends React.PureComponent<MoreMessagesB
|
||||
id: t('mobile.more_messages_button.text'),
|
||||
defaultMessage: '{count} {isInitialMessage, select, true {new} other {more new}} {count, plural, one {message} other {messages}}',
|
||||
}, {isInitialMessage, count});
|
||||
}
|
||||
};
|
||||
|
||||
onScrollEndIndex = (endIndex: number) => {
|
||||
this.pressed = false;
|
||||
this.endIndex = endIndex;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {theme, loadingPosts, testID} = this.props;
|
||||
|
||||
@@ -84,7 +84,7 @@ const Body = ({
|
||||
const isReplyPost = Boolean(post.root_id && (!isPostEphemeral(post) || post.state === Posts.POST_DELETED) && location !== THREAD);
|
||||
const hasContent = (post.metadata?.embeds?.length || (appsEnabled && post.props?.app_bindings?.length)) || post.props?.attachments?.length;
|
||||
|
||||
const replyBarStyle = useCallback((): StyleProp<ViewStyle>[]|undefined => {
|
||||
const replyBarStyle = useCallback((): Array<StyleProp<ViewStyle>>|undefined => {
|
||||
if (!isReplyPost) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ type Props = {
|
||||
theme: Theme;
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme:Theme) => {
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
||||
return {
|
||||
container: {
|
||||
borderBottomColor: changeOpacity(theme.centerChannelColor, 0.15),
|
||||
|
||||
@@ -103,7 +103,7 @@ const MenuBinding = ({binding, doAppCall, intl, post, postEphemeralCallResponseF
|
||||
}
|
||||
}, [theme]);
|
||||
|
||||
const options = binding.bindings?.map<PostActionOption>((b:AppBinding) => {
|
||||
const options = binding.bindings?.map<PostActionOption>((b: AppBinding) => {
|
||||
return {text: b.label, value: b.location || ''};
|
||||
});
|
||||
|
||||
|
||||
@@ -12,10 +12,10 @@ import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
type Props = {
|
||||
baseTextStyle: StyleProp<TextStyle>;
|
||||
blockStyles?: StyleProp<ViewStyle>[];
|
||||
blockStyles?: Array<StyleProp<ViewStyle>>;
|
||||
fields: MessageAttachmentField[];
|
||||
metadata?: PostMetadata;
|
||||
textStyles?: StyleProp<TextStyle>[];
|
||||
textStyles?: Array<StyleProp<TextStyle>>;
|
||||
theme: Theme;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@ import {PostMetadata} from '@mm-redux/types/posts';
|
||||
|
||||
type Props = {
|
||||
baseTextStyle: StyleProp<TextStyle>;
|
||||
blockStyles?: StyleProp<ViewStyle>[];
|
||||
blockStyles?: Array<StyleProp<ViewStyle>>;
|
||||
metadata?: PostMetadata;
|
||||
textStyles?: StyleProp<TextStyle>[];
|
||||
textStyles?: Array<StyleProp<TextStyle>>;
|
||||
value?: string;
|
||||
}
|
||||
export default function AttachmentPreText(props: Props) {
|
||||
|
||||
@@ -13,10 +13,10 @@ import {Theme} from '@mm-redux/types/theme';
|
||||
|
||||
type Props = {
|
||||
baseTextStyle: StyleProp<TextStyle>;
|
||||
blockStyles?: StyleProp<ViewStyle>[];
|
||||
blockStyles?: Array<StyleProp<ViewStyle>>;
|
||||
hasThumbnail?: boolean;
|
||||
metadata?: PostMetadata;
|
||||
textStyles?: StyleProp<TextStyle>[];
|
||||
textStyles?: Array<StyleProp<TextStyle>>;
|
||||
theme: Theme;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
@@ -59,8 +59,8 @@ type PostListProps = {
|
||||
}
|
||||
|
||||
type ViewableItemsChanged = {
|
||||
viewableItems: Array<ViewToken>;
|
||||
changed: Array<ViewToken>;
|
||||
viewableItems: ViewToken[];
|
||||
changed: ViewToken[];
|
||||
}
|
||||
|
||||
type onScrollEndIndexListenerEvent = (endIndex: number) => void;
|
||||
|
||||
@@ -55,7 +55,7 @@ export default class ProgressiveImage extends PureComponent {
|
||||
this.setState({
|
||||
showHighResImage: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onLoadImageEnd = () => {
|
||||
Animated.timing(this.state.intensity, {
|
||||
@@ -63,7 +63,7 @@ export default class ProgressiveImage extends PureComponent {
|
||||
toValue: 100,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
|
||||
@@ -23,7 +23,7 @@ export default class ReactionButton extends PureComponent {
|
||||
emoji: PropTypes.string.isRequired,
|
||||
iconSize: PropTypes.number,
|
||||
containerSize: PropTypes.number,
|
||||
}
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
iconSize: LARGE_ICON_SIZE,
|
||||
@@ -42,7 +42,7 @@ export default class ReactionButton extends PureComponent {
|
||||
this.setState({isSelected: true}, () => {
|
||||
this.props.addReaction(this.props.emoji);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
|
||||
@@ -17,7 +17,7 @@ type RetriableFastImageState = {
|
||||
export default class RetriableFastImage extends PureComponent<RetriableFastImageProps, RetriableFastImageState> {
|
||||
state = {
|
||||
retry: 0,
|
||||
}
|
||||
};
|
||||
|
||||
onError = () => {
|
||||
const retry = this.state.retry + 1;
|
||||
@@ -27,7 +27,7 @@ export default class RetriableFastImage extends PureComponent<RetriableFastImage
|
||||
}
|
||||
|
||||
this.setState({retry});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
|
||||
@@ -45,7 +45,7 @@ export default class Root extends PureComponent {
|
||||
|
||||
setProviderRef = (ref) => {
|
||||
this.providerRef = ref;
|
||||
}
|
||||
};
|
||||
|
||||
handleNoTeams = () => {
|
||||
if (!this.providerRef) {
|
||||
@@ -61,7 +61,7 @@ export default class Root extends PureComponent {
|
||||
return;
|
||||
}
|
||||
this.navigateToTeamsPage('ErrorTeamsList', true);
|
||||
}
|
||||
};
|
||||
|
||||
navigateToTeamsPage = (screen, error = false) => {
|
||||
const {currentUrl, theme} = this.props;
|
||||
@@ -98,7 +98,7 @@ export default class Root extends PureComponent {
|
||||
}
|
||||
|
||||
resetToTeams(screen, title, passProps, options);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const locale = getLocaleFromLanguage(this.props.locale);
|
||||
|
||||
@@ -9,7 +9,7 @@ import type {Theme} from '@mm-redux/types/theme';
|
||||
|
||||
type SafeAreaProps = {
|
||||
backgroundColor?: string;
|
||||
children: Array<React.ReactChild> | React.ReactChild;
|
||||
children: React.ReactChild[] | React.ReactChild;
|
||||
excludeHeader?: boolean;
|
||||
excludeFooter?: boolean;
|
||||
excludeLeft?: boolean;
|
||||
|
||||
@@ -98,11 +98,11 @@ export default class Search extends PureComponent {
|
||||
|
||||
setSearchContainerRef = (ref) => {
|
||||
this.searchContainerRef = ref;
|
||||
}
|
||||
};
|
||||
|
||||
setInputKeywordRef = (ref) => {
|
||||
this.inputKeywordRef = ref;
|
||||
}
|
||||
};
|
||||
|
||||
blur = () => {
|
||||
this.inputKeywordRef.blur();
|
||||
@@ -188,7 +188,7 @@ export default class Search extends PureComponent {
|
||||
),
|
||||
]).start(resolve);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
collapseAnimation = () => {
|
||||
return new Promise((resolve) => {
|
||||
@@ -209,7 +209,7 @@ export default class Search extends PureComponent {
|
||||
),
|
||||
]).start(resolve);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {testID, backgroundColor, inputHeight, inputStyle, placeholderTextColor, tintColorSearch, cancelButtonStyle, tintColorDelete, titleCancelColor, searchBarRightMargin, containerHeight} = this.props;
|
||||
|
||||
@@ -56,7 +56,7 @@ export default class ChannelsList extends PureComponent {
|
||||
|
||||
setSearchBarRef = (ref) => {
|
||||
this.searchBarRef = ref;
|
||||
}
|
||||
};
|
||||
|
||||
cancelSearch = () => {
|
||||
if (this.searchBarRef) {
|
||||
|
||||
@@ -230,7 +230,7 @@ class FilteredList extends Component {
|
||||
});
|
||||
|
||||
return this.filterChannels([...favoriteDms, ...dms, ...groupChannels], term).sort(sortChannelsByDisplayName.bind(null, props.intl.locale));
|
||||
}
|
||||
};
|
||||
|
||||
buildMembersForSearch = (props, term) => {
|
||||
const {channels, currentUserId, teammateNameDisplay, profiles, teamProfiles, pastDirectMessages, restrictDms} = props;
|
||||
@@ -266,7 +266,7 @@ class FilteredList extends Component {
|
||||
const fakeDms = this.filterChannels([...members], term);
|
||||
|
||||
return [...fakeDms].sort(sortChannelsByDisplayName.bind(null, props.intl.locale));
|
||||
}
|
||||
};
|
||||
|
||||
buildChannelsForSearch = (props, term) => {
|
||||
const {
|
||||
@@ -281,7 +281,7 @@ class FilteredList extends Component {
|
||||
|
||||
return this.filterChannels([...favorites, ...publicChannels, ...privateChannels], term).
|
||||
sort(sortChannelsByDisplayName.bind(null, props.intl.locale));
|
||||
}
|
||||
};
|
||||
|
||||
buildArchivedForSearch = (props, term) => {
|
||||
const {currentChannel, archivedChannels} = props;
|
||||
@@ -294,7 +294,7 @@ class FilteredList extends Component {
|
||||
|
||||
return acc;
|
||||
}, []), term);
|
||||
}
|
||||
};
|
||||
|
||||
buildOtherMembersForSearch = (props, term) => {
|
||||
const {otherChannels} = props;
|
||||
@@ -307,7 +307,7 @@ class FilteredList extends Component {
|
||||
});
|
||||
|
||||
return this.filterChannels(notMemberOf, term);
|
||||
}
|
||||
};
|
||||
|
||||
buildSectionsForSearch = (props, term) => {
|
||||
const sections = [];
|
||||
|
||||
@@ -132,7 +132,7 @@ export default class List extends PureComponent {
|
||||
|
||||
setListRef = (ref) => {
|
||||
this.listRef = ref;
|
||||
}
|
||||
};
|
||||
|
||||
getSectionConfigByType = (props, sectionType) => {
|
||||
const {canCreatePrivateChannels, canJoinPublicChannels} = props;
|
||||
@@ -463,7 +463,7 @@ export default class List extends PureComponent {
|
||||
{header}
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
buildCategorySections = () => {
|
||||
const categoriesBySection = [];
|
||||
@@ -496,7 +496,7 @@ export default class List extends PureComponent {
|
||||
}
|
||||
|
||||
return categoriesBySection;
|
||||
}
|
||||
};
|
||||
|
||||
scrollToTop = () => {
|
||||
//eslint-disable-next-line no-underscore-dangle
|
||||
|
||||
@@ -53,7 +53,7 @@ export default class DrawerSwiper extends Component {
|
||||
if (this.swiperRef?.current) {
|
||||
this.swiperRef.current.scrollToInitial();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
swiperPageSelected = (index) => {
|
||||
this.props.onPageSelected(index);
|
||||
|
||||
@@ -57,7 +57,7 @@ export default class MainSidebarAndroid extends MainSidebarBase {
|
||||
|
||||
handleSidebarDidOpen = () => {
|
||||
this.setState({opened: true});
|
||||
}
|
||||
};
|
||||
|
||||
onPageSelected = (index) => {
|
||||
this.swiperIndex = index;
|
||||
@@ -69,7 +69,7 @@ export default class MainSidebarAndroid extends MainSidebarBase {
|
||||
|
||||
setProviderRef = (ref) => {
|
||||
this.providerRef = ref;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const locale = this.props.locale;
|
||||
|
||||
@@ -278,7 +278,7 @@ export default class MainSidebarBase extends Component {
|
||||
const {setCategoryCollapsed} = this.props.actions;
|
||||
|
||||
setCategoryCollapsed(categoryId, collapse);
|
||||
}
|
||||
};
|
||||
|
||||
selectChannel = (channel, currentChannelId, closeDrawer = true) => {
|
||||
const {handleSelectChannel, handleNotViewingGlobalThreadsScreen} = this.props.actions;
|
||||
|
||||
@@ -50,7 +50,7 @@ export default class SettingsDrawerAndroid extends SettingsSidebarBase {
|
||||
goToCustomStatus = preventDoubleTap(() => {
|
||||
const {intl} = this.providerRef.getChildContext();
|
||||
this.goToCustomStatusScreen(intl);
|
||||
})
|
||||
});
|
||||
|
||||
renderNavigationView = () => {
|
||||
const {theme} = this.props;
|
||||
@@ -65,7 +65,7 @@ export default class SettingsDrawerAndroid extends SettingsSidebarBase {
|
||||
|
||||
setProviderRef = (ref) => {
|
||||
this.providerRef = ref;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const locale = this.props.locale;
|
||||
|
||||
@@ -115,7 +115,7 @@ export default class SettingsDrawer extends SettingsSidebarBase {
|
||||
goToCustomStatus = preventDoubleTap(() => {
|
||||
const {intl} = this.context;
|
||||
this.goToCustomStatusScreen(intl);
|
||||
})
|
||||
});
|
||||
|
||||
renderNavigationView = () => {
|
||||
const {theme} = this.props;
|
||||
|
||||
@@ -74,7 +74,7 @@ export default class SettingsSidebarBase extends PureComponent {
|
||||
this.setState({
|
||||
showRetryMessage: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleCustomStatusChange = (prevCustomStatus, customStatus) => {
|
||||
const isStatusSet = Boolean(customStatus?.emoji);
|
||||
@@ -87,7 +87,7 @@ export default class SettingsSidebarBase extends PureComponent {
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
confirmResetBase = (status, intl) => {
|
||||
confirmOutOfOfficeDisabled(intl, status, this.updateStatus);
|
||||
@@ -169,7 +169,7 @@ export default class SettingsSidebarBase extends PureComponent {
|
||||
goToCustomStatusScreen = (intl) => {
|
||||
this.closeSettingsSidebar();
|
||||
showModal('CustomStatus', intl.formatMessage({id: 'mobile.routes.custom_status', defaultMessage: 'Set a Status'}));
|
||||
}
|
||||
};
|
||||
|
||||
logout = preventDoubleTap(() => {
|
||||
const {logout} = this.props.actions;
|
||||
@@ -239,7 +239,7 @@ export default class SettingsSidebarBase extends PureComponent {
|
||||
if (error) {
|
||||
this.setState({showStatus: true, showRetryMessage: true});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
renderCustomStatus = () => {
|
||||
const {isCustomStatusEnabled, customStatus, theme, isCustomStatusExpired, isCustomStatusExpirySupported} = this.props;
|
||||
|
||||
@@ -24,7 +24,7 @@ export default class TextInputWithLocalizedPlaceholder extends PureComponent {
|
||||
|
||||
setInputRef = (ref) => {
|
||||
this.inputRef = ref;
|
||||
}
|
||||
};
|
||||
|
||||
blur = () => {
|
||||
this.inputRef.blur();
|
||||
|
||||
@@ -32,7 +32,7 @@ export default class RadioSetting extends PureComponent {
|
||||
const {onChange, id} = this.props;
|
||||
onChange(id, item);
|
||||
this.setState({value: item});
|
||||
}
|
||||
};
|
||||
|
||||
renderCheckMark = (value, style) => {
|
||||
if (value === this.state.value) {
|
||||
@@ -44,7 +44,7 @@ export default class RadioSetting extends PureComponent {
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
renderRowSeparator = (idx, separatorStyle) => {
|
||||
const {options} = this.props;
|
||||
@@ -52,7 +52,7 @@ export default class RadioSetting extends PureComponent {
|
||||
return null;
|
||||
}
|
||||
return <View style={separatorStyle}/>;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
|
||||
@@ -6,7 +6,7 @@ import {StyleProp, TextStyle, ViewStyle} from 'react-native';
|
||||
|
||||
interface CustomPropTypes {
|
||||
Children: ReactNode | ReactNodeArray;
|
||||
Style: object | number | StyleProp<TextStyle> | StyleProp<TextStyle>[] | StyleProp<ViewStyle> | StyleProp<ViewStyle>[];
|
||||
Style: object | number | StyleProp<TextStyle> | Array<StyleProp<TextStyle>> | StyleProp<ViewStyle> | Array<StyleProp<ViewStyle>>;
|
||||
}
|
||||
|
||||
export default CustomPropTypes;
|
||||
|
||||
@@ -169,7 +169,7 @@ class EMMProvider {
|
||||
{cancelable: false, onDismiss: resolve},
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default new EMMProvider();
|
||||
|
||||
@@ -13,6 +13,7 @@ import ClientError from '@client/rest/error';
|
||||
import mattermostManaged from '@mattermost-managed';
|
||||
import {General} from '@mm-redux/constants';
|
||||
import EventEmitter from '@mm-redux/utils/event_emitter';
|
||||
import {semverFromServerVersion} from '@utils/general';
|
||||
import {t} from '@utils/i18n';
|
||||
|
||||
import mattermostBucket from 'app/mattermost_bucket';
|
||||
@@ -116,7 +117,7 @@ Client4.doFetchWithResponse = async (url, options) => {
|
||||
Client4.setToken(token);
|
||||
}
|
||||
|
||||
const serverVersion = headers[HEADER_X_VERSION_ID] || headers[HEADER_X_VERSION_ID.toLowerCase()];
|
||||
const serverVersion = semverFromServerVersion(headers[HEADER_X_VERSION_ID] || headers[HEADER_X_VERSION_ID.toLowerCase()]);
|
||||
if (serverVersion && !headers['Cache-Control'] && Client4.serverVersion !== serverVersion) {
|
||||
Client4.serverVersion = serverVersion; /* eslint-disable-line require-atomic-updates */
|
||||
EventEmitter.emit(General.SERVER_VERSION_CHANGED, serverVersion);
|
||||
|
||||
@@ -29,7 +29,7 @@ describe('Fetch', () => {
|
||||
test('doFetchWithResponse handles title case headers', async () => {
|
||||
const setToken = jest.spyOn(Client4, 'setToken');
|
||||
const headers = {
|
||||
[HEADER_X_VERSION_ID]: 'VersionId',
|
||||
[HEADER_X_VERSION_ID]: '6.1.0',
|
||||
[HEADER_X_CLUSTER_ID]: 'ClusterId',
|
||||
[HEADER_TOKEN]: 'Token',
|
||||
};
|
||||
@@ -48,7 +48,7 @@ describe('Fetch', () => {
|
||||
test('doFetchWithResponse handles lower case headers', async () => {
|
||||
const setToken = jest.spyOn(Client4, 'setToken');
|
||||
const headers = {
|
||||
[HEADER_X_VERSION_ID.toLowerCase()]: 'versionid',
|
||||
[HEADER_X_VERSION_ID.toLowerCase()]: '6.2.0',
|
||||
[HEADER_X_CLUSTER_ID.toLowerCase()]: 'clusterid',
|
||||
[HEADER_TOKEN.toLowerCase()]: 'token',
|
||||
};
|
||||
@@ -66,7 +66,7 @@ describe('Fetch', () => {
|
||||
|
||||
test('doFetchWithResponse handles server version change', async () => {
|
||||
const emit = jest.spyOn(EventEmitter, 'emit');
|
||||
const serverVersion1 = 'version1';
|
||||
const serverVersion1 = '6.3.0';
|
||||
const response = {
|
||||
json: () => Promise.resolve('data'),
|
||||
ok: true,
|
||||
|
||||
@@ -7,6 +7,7 @@ import CookieManager from '@react-native-cookies/cookies';
|
||||
import {AppState, Dimensions, Keyboard, Linking, Platform} from 'react-native';
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
import {getLocales} from 'react-native-localize';
|
||||
import {valid as validVersion} from 'semver';
|
||||
|
||||
import {setDeviceDimensions, setDeviceOrientation, setDeviceAsTablet} from '@actions/device';
|
||||
import {dismissAllModals, popToRoot, showOverlay} from '@actions/navigation';
|
||||
@@ -249,9 +250,11 @@ class GlobalEventHandler {
|
||||
onServerVersionChanged = async (serverVersion) => {
|
||||
const {dispatch, getState} = Store.redux;
|
||||
const state = getState();
|
||||
if (serverVersion && state.entities.users && state.entities.users.currentUserId) {
|
||||
const {general, users} = state.entities;
|
||||
const isValid = validVersion(serverVersion);
|
||||
const versionDidChange = general?.serverVersion !== serverVersion;
|
||||
if (isValid && serverVersion && versionDidChange && users?.currentUserId) {
|
||||
dispatch(setServerVersion(serverVersion));
|
||||
dispatch(loadConfigAndLicense());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -302,7 +305,7 @@ class GlobalEventHandler {
|
||||
// clear error
|
||||
return e;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
serverUpgradeNeeded = async () => {
|
||||
const {dispatch} = Store.redux;
|
||||
@@ -322,12 +325,12 @@ class GlobalEventHandler {
|
||||
this.pushNotificationListener = true;
|
||||
EventEmitter.on(ViewTypes.NOTIFICATION_IN_APP, this.handleInAppNotification);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
turnOffInAppNotificationHandling = () => {
|
||||
this.pushNotificationListener = false;
|
||||
EventEmitter.off(ViewTypes.NOTIFICATION_IN_APP, this.handleInAppNotification);
|
||||
}
|
||||
};
|
||||
|
||||
handleInAppNotification = (notification) => {
|
||||
const {payload} = notification;
|
||||
|
||||
@@ -147,6 +147,9 @@ describe('GlobalEventHandler', () => {
|
||||
const currentUserId = 'current-user-id';
|
||||
Store.redux.getState = jest.fn().mockReturnValue({
|
||||
entities: {
|
||||
general: {
|
||||
serverVersion: '',
|
||||
},
|
||||
users: {
|
||||
currentUserId,
|
||||
profiles: {
|
||||
@@ -164,21 +167,18 @@ describe('GlobalEventHandler', () => {
|
||||
const invalidVersion = 'a.b.c';
|
||||
await GlobalEventHandler.onServerVersionChanged(invalidVersion);
|
||||
|
||||
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||
expect(dispatch).toHaveBeenCalledWith('setServerVersion');
|
||||
expect(dispatch).toHaveBeenCalledWith('loadConfigAndLicense');
|
||||
expect(dispatch).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('should dispatch on gte min server version with currentUserId', async () => {
|
||||
let version = minVersion.version;
|
||||
await GlobalEventHandler.onServerVersionChanged(version);
|
||||
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||
expect(dispatch).toHaveBeenCalledWith('setServerVersion');
|
||||
expect(dispatch).toHaveBeenCalledWith('loadConfigAndLicense');
|
||||
|
||||
version = semver.coerce(minVersion.major + 1).version;
|
||||
await GlobalEventHandler.onServerVersionChanged(version);
|
||||
expect(dispatch).toHaveBeenCalledTimes(4);
|
||||
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should not dispatch on empty, null, undefined server version', async () => {
|
||||
|
||||
@@ -68,7 +68,7 @@ class PushNotifications {
|
||||
return AndroidNotificationPreferences.getDeliveredNotifications();
|
||||
}
|
||||
return Notifications.ios.getDeliveredNotifications() as Promise<NotificationWithChannel[]>;
|
||||
}
|
||||
};
|
||||
|
||||
cancelAllLocalNotifications() {
|
||||
Notifications.cancelAllLocalNotifications();
|
||||
@@ -132,7 +132,7 @@ class PushNotifications {
|
||||
} else {
|
||||
Notifications.ios.removeDeliveredNotifications(notificationIds);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setBadgeCountByMentions = (initialBadge = 0) => {
|
||||
let badgeCount = initialBadge;
|
||||
@@ -148,7 +148,7 @@ class PushNotifications {
|
||||
badgeCount = badgeCount <= 0 ? 0 : badgeCount;
|
||||
Notifications.ios.setBadgeCount(badgeCount);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
createReplyCategory = () => {
|
||||
const {getState} = Store.redux!;
|
||||
@@ -164,7 +164,7 @@ class PushNotifications {
|
||||
};
|
||||
const replyAction = new NotificationAction(REPLY_ACTION, 'background', replyTitle, true, replyTextInput);
|
||||
return new NotificationCategory(CATEGORY, [replyAction]);
|
||||
}
|
||||
};
|
||||
|
||||
getInitialNotification = async () => {
|
||||
const notification: NotificationWithData | undefined = await Notifications.getInitialNotification();
|
||||
@@ -173,6 +173,17 @@ class PushNotifications {
|
||||
EphemeralStore.setStartFromNotification(true);
|
||||
notification.userInteraction = true;
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
// when a notification is received on iOS, getInitialNotification, will return the notification
|
||||
// as the app will initialized cause we are using background fetch,
|
||||
// that does not necessarily mean that the app was opened cause of the notification was tapped.
|
||||
// Here we are going to dettermine if the notification still exists in NotificationCenter to verify if
|
||||
// the app was opened because of a tap or cause of the background fetch init
|
||||
const delivered = await Notifications.ios.getDeliveredNotifications();
|
||||
notification.userInteraction = delivered.find((d) => (d as unknown as NotificationWithAck).ack_id === notification?.payload?.ack_id) == null;
|
||||
notification.foreground = false;
|
||||
}
|
||||
|
||||
// getInitialNotification may run before the store is set
|
||||
// that is why we run on an interval until the store is available
|
||||
// once we handle the notification the interval is cleared.
|
||||
@@ -183,7 +194,7 @@ class PushNotifications {
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleNotification = (notification: NotificationWithData, isInitialNotification = false) => {
|
||||
const {payload, foreground, userInteraction} = notification;
|
||||
@@ -221,6 +232,8 @@ class PushNotifications {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!userInteraction && Platform.OS === 'ios') {
|
||||
dispatch(loadFromPushNotification(notification, isInitialNotification, true));
|
||||
}
|
||||
break;
|
||||
case NOTIFICATION_TYPE.SESSION:
|
||||
@@ -231,17 +244,17 @@ class PushNotifications {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
localNotification = (notification: Notification) => {
|
||||
Notifications.postLocalNotification(notification);
|
||||
}
|
||||
};
|
||||
|
||||
onNotificationOpened = (notification: NotificationWithData, completion: () => void) => {
|
||||
notification.userInteraction = true;
|
||||
this.handleNotification(notification);
|
||||
completion();
|
||||
}
|
||||
};
|
||||
|
||||
onNotificationReceivedBackground = (notification: NotificationWithData, completion: (response: NotificationBackgroundFetchResult) => void) => {
|
||||
this.handleNotification(notification);
|
||||
|
||||
@@ -159,7 +159,7 @@ export function markGroupChannelOpen(channelId: string): ActionFunc {
|
||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||
const {currentUserId} = getState().entities.users;
|
||||
|
||||
const preferences: Array<PreferenceType> = [
|
||||
const preferences: PreferenceType[] = [
|
||||
{user_id: currentUserId, category: Preferences.CATEGORY_GROUP_CHANNEL_SHOW, name: channelId, value: 'true'},
|
||||
{user_id: currentUserId, category: Preferences.CATEGORY_CHANNEL_OPEN_TIME, name: channelId, value: new Date().getTime().toString()},
|
||||
];
|
||||
@@ -168,7 +168,7 @@ export function markGroupChannelOpen(channelId: string): ActionFunc {
|
||||
};
|
||||
}
|
||||
|
||||
export function createGroupChannel(userIds: Array<string>): ActionFunc {
|
||||
export function createGroupChannel(userIds: string[]): ActionFunc {
|
||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||
dispatch({type: ChannelTypes.CREATE_CHANNEL_REQUEST, data: null});
|
||||
|
||||
@@ -1357,7 +1357,7 @@ export function markChannelAsRead(channelId: string, prevChannelId?: string, upd
|
||||
|
||||
// Increments the number of posts in the channel by 1 and marks it as unread if necessary
|
||||
|
||||
export function markChannelAsUnread(teamId: string, channelId: string, mentions: Array<string>): ActionFunc {
|
||||
export function markChannelAsUnread(teamId: string, channelId: string, mentions: string[]): ActionFunc {
|
||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||
const state = getState();
|
||||
const {myMembers} = state.entities.channels;
|
||||
@@ -1397,7 +1397,7 @@ export function markChannelAsUnread(teamId: string, channelId: string, mentions:
|
||||
};
|
||||
}
|
||||
|
||||
export function getChannelMembersByIds(channelId: string, userIds: Array<string>) {
|
||||
export function getChannelMembersByIds(channelId: string, userIds: string[]) {
|
||||
return bindClientFunc({
|
||||
clientFunc: Client4.getChannelMembersByIds,
|
||||
onSuccess: ChannelTypes.RECEIVED_CHANNEL_MEMBERS,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user