forked from Ivasoft/mattermost-mobile
Compare commits
1 Commits
test1.0.2
...
MM-50010-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2bcea5a6a3 |
31
.drone.yml
31
.drone.yml
@@ -1,31 +0,0 @@
|
|||||||
kind: pipeline
|
|
||||||
name: default
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: permissions
|
|
||||||
image: alpine/git
|
|
||||||
commands:
|
|
||||||
- chmod -R 777 .
|
|
||||||
|
|
||||||
#- name: build
|
|
||||||
# image: cimg/android:2022.09.2-node
|
|
||||||
# environment:
|
|
||||||
# CIRCLECI: true
|
|
||||||
# NODE_OPTIONS: --max_old_space_size=12000
|
|
||||||
# NODE_ENV: production
|
|
||||||
# BABEL_ENV: production
|
|
||||||
# MATTERMOST_RELEASE_STORE_FILE: /root/mattermost.keystore
|
|
||||||
# MATTERMOST_RELEASE_KEY_ALIAS: mattermost-google-key
|
|
||||||
# MATTERMOST_RELEASE_PASSWORD: 123456
|
|
||||||
# commands:
|
|
||||||
# - 'npm run build:android'
|
|
||||||
|
|
||||||
- name: gitea_release
|
|
||||||
image: plugins/gitea-release
|
|
||||||
settings:
|
|
||||||
api_key:
|
|
||||||
from_secret: drone_release
|
|
||||||
base_url: https://git.ivasoft.cz
|
|
||||||
files: package.json
|
|
||||||
when:
|
|
||||||
event: tag
|
|
||||||
@@ -110,7 +110,7 @@ android {
|
|||||||
applicationId "com.mattermost.rnbeta"
|
applicationId "com.mattermost.rnbeta"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 461
|
versionCode 460
|
||||||
versionName "2.1.0"
|
versionName "2.1.0"
|
||||||
testBuildType System.getProperty('testBuildType', 'debug')
|
testBuildType System.getProperty('testBuildType', 'debug')
|
||||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {Platform} from 'react-native';
|
|||||||
|
|
||||||
import {WebsocketEvents} from '@constants';
|
import {WebsocketEvents} from '@constants';
|
||||||
import DatabaseManager from '@database/manager';
|
import DatabaseManager from '@database/manager';
|
||||||
import {getConfigValue} from '@queries/servers/system';
|
import {getConfig} from '@queries/servers/system';
|
||||||
import {hasReliableWebsocket} from '@utils/config';
|
import {hasReliableWebsocket} from '@utils/config';
|
||||||
import {toMilliseconds} from '@utils/datetime';
|
import {toMilliseconds} from '@utils/datetime';
|
||||||
import {logError, logInfo, logWarning} from '@utils/log';
|
import {logError, logInfo, logWarning} from '@utils/log';
|
||||||
@@ -79,12 +79,8 @@ export default class WebSocketClient {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [websocketUrl, version, reliableWebsocketConfig] = await Promise.all([
|
const config = await getConfig(database);
|
||||||
getConfigValue(database, 'WebsocketURL'),
|
const connectionUrl = (config.WebsocketURL || this.serverUrl) + '/api/v4/websocket';
|
||||||
getConfigValue(database, 'Version'),
|
|
||||||
getConfigValue(database, 'EnableReliableWebSockets'),
|
|
||||||
]);
|
|
||||||
const connectionUrl = (websocketUrl || this.serverUrl) + '/api/v4/websocket';
|
|
||||||
|
|
||||||
if (this.connectingCallback) {
|
if (this.connectingCallback) {
|
||||||
this.connectingCallback();
|
this.connectingCallback();
|
||||||
@@ -105,7 +101,7 @@ export default class WebSocketClient {
|
|||||||
|
|
||||||
this.url = connectionUrl;
|
this.url = connectionUrl;
|
||||||
|
|
||||||
const reliableWebSockets = hasReliableWebsocket(version, reliableWebsocketConfig);
|
const reliableWebSockets = hasReliableWebsocket(config);
|
||||||
if (reliableWebSockets) {
|
if (reliableWebSockets) {
|
||||||
// Add connection id, and last_sequence_number to the query param.
|
// Add connection id, and last_sequence_number to the query param.
|
||||||
// We cannot also send it as part of the auth_challenge, because the session cookie is already sent with the request.
|
// We cannot also send it as part of the auth_challenge, because the session cookie is already sent with the request.
|
||||||
@@ -133,11 +129,6 @@ export default class WebSocketClient {
|
|||||||
headers.Authorization = `Bearer ${this.token}`;
|
headers.Authorization = `Bearer ${this.token}`;
|
||||||
}
|
}
|
||||||
const {client} = await getOrCreateWebSocketClient(this.url, {headers, timeoutInterval: WEBSOCKET_TIMEOUT});
|
const {client} = await getOrCreateWebSocketClient(this.url, {headers, timeoutInterval: WEBSOCKET_TIMEOUT});
|
||||||
|
|
||||||
// Check again if the client is the same, to avoid race conditions
|
|
||||||
if (this.conn === client) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.conn = client;
|
this.conn = client;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import TeamList from './team_list';
|
|||||||
type Props = {
|
type Props = {
|
||||||
iconPad?: boolean;
|
iconPad?: boolean;
|
||||||
canJoinOtherTeams: boolean;
|
canJoinOtherTeams: boolean;
|
||||||
hasMoreThanOneTeam: boolean;
|
teamsCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
||||||
@@ -36,8 +36,8 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function TeamSidebar({iconPad, canJoinOtherTeams, hasMoreThanOneTeam}: Props) {
|
export default function TeamSidebar({iconPad, canJoinOtherTeams, teamsCount}: Props) {
|
||||||
const initialWidth = hasMoreThanOneTeam ? TEAM_SIDEBAR_WIDTH : 0;
|
const initialWidth = teamsCount > 1 ? TEAM_SIDEBAR_WIDTH : 0;
|
||||||
const width = useSharedValue(initialWidth);
|
const width = useSharedValue(initialWidth);
|
||||||
const marginTop = useSharedValue(iconPad ? 44 : 0);
|
const marginTop = useSharedValue(iconPad ? 44 : 0);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@@ -58,8 +58,8 @@ export default function TeamSidebar({iconPad, canJoinOtherTeams, hasMoreThanOneT
|
|||||||
}, [iconPad]);
|
}, [iconPad]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
width.value = hasMoreThanOneTeam ? TEAM_SIDEBAR_WIDTH : 0;
|
width.value = teamsCount > 1 ? TEAM_SIDEBAR_WIDTH : 0;
|
||||||
}, [hasMoreThanOneTeam]);
|
}, [teamsCount]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Animated.View style={[styles.container, transform]}>
|
<Animated.View style={[styles.container, transform]}>
|
||||||
|
|||||||
@@ -43,14 +43,13 @@ import type {
|
|||||||
ApiResp,
|
ApiResp,
|
||||||
Call,
|
Call,
|
||||||
CallParticipant,
|
CallParticipant,
|
||||||
CallReactionEmoji,
|
|
||||||
CallsConnection,
|
CallsConnection,
|
||||||
RecordingState,
|
|
||||||
ServerCallState,
|
ServerCallState,
|
||||||
ServerChannelState,
|
ServerChannelState,
|
||||||
} from '@calls/types/calls';
|
} from '@calls/types/calls';
|
||||||
import type {Client} from '@client/rest';
|
import type {Client} from '@client/rest';
|
||||||
import type ClientError from '@client/rest/error';
|
import type ClientError from '@client/rest/error';
|
||||||
|
import type {CallRecordingState, EmojiData} from '@mmcalls/common/lib/types';
|
||||||
import type {IntlShape} from 'react-intl';
|
import type {IntlShape} from 'react-intl';
|
||||||
|
|
||||||
let connection: CallsConnection | null = null;
|
let connection: CallsConnection | null = null;
|
||||||
@@ -322,7 +321,7 @@ export const unraiseHand = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const sendReaction = (emoji: CallReactionEmoji) => {
|
export const sendReaction = (emoji: EmojiData) => {
|
||||||
if (connection) {
|
if (connection) {
|
||||||
connection.sendReaction(emoji);
|
connection.sendReaction(emoji);
|
||||||
}
|
}
|
||||||
@@ -415,7 +414,7 @@ export const startCallRecording = async (serverUrl: string, callId: string) => {
|
|||||||
|
|
||||||
const client = NetworkManager.getClient(serverUrl);
|
const client = NetworkManager.getClient(serverUrl);
|
||||||
|
|
||||||
let data: ApiResp | RecordingState;
|
let data: ApiResp | CallRecordingState;
|
||||||
try {
|
try {
|
||||||
data = await client.startCallRecording(callId);
|
data = await client.startCallRecording(callId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -433,7 +432,7 @@ export const stopCallRecording = async (serverUrl: string, callId: string) => {
|
|||||||
|
|
||||||
const client = NetworkManager.getClient(serverUrl);
|
const client = NetworkManager.getClient(serverUrl);
|
||||||
|
|
||||||
let data: ApiResp | RecordingState;
|
let data: ApiResp | CallRecordingState;
|
||||||
try {
|
try {
|
||||||
data = await client.stopCallRecording(callId);
|
data = await client.stopCallRecording(callId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,24 +1,20 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import type {
|
import type {ServerChannelState, ApiResp} from '@calls/types/calls';
|
||||||
ServerChannelState,
|
import type {CallRecordingState, CallsConfig} from '@mmcalls/common/lib/types';
|
||||||
ServerCallsConfig,
|
|
||||||
ApiResp,
|
|
||||||
RecordingState,
|
|
||||||
} from '@calls/types/calls';
|
|
||||||
import type {RTCIceServer} from 'react-native-webrtc';
|
import type {RTCIceServer} from 'react-native-webrtc';
|
||||||
|
|
||||||
export interface ClientCallsMix {
|
export interface ClientCallsMix {
|
||||||
getEnabled: () => Promise<Boolean>;
|
getEnabled: () => Promise<Boolean>;
|
||||||
getCalls: () => Promise<ServerChannelState[]>;
|
getCalls: () => Promise<ServerChannelState[]>;
|
||||||
getCallForChannel: (channelId: string) => Promise<ServerChannelState>;
|
getCallForChannel: (channelId: string) => Promise<ServerChannelState>;
|
||||||
getCallsConfig: () => Promise<ServerCallsConfig>;
|
getCallsConfig: () => Promise<CallsConfig>;
|
||||||
enableChannelCalls: (channelId: string, enable: boolean) => Promise<ServerChannelState>;
|
enableChannelCalls: (channelId: string, enable: boolean) => Promise<ServerChannelState>;
|
||||||
endCall: (channelId: string) => Promise<ApiResp>;
|
endCall: (channelId: string) => Promise<ApiResp>;
|
||||||
genTURNCredentials: () => Promise<RTCIceServer[]>;
|
genTURNCredentials: () => Promise<RTCIceServer[]>;
|
||||||
startCallRecording: (callId: string) => Promise<ApiResp | RecordingState>;
|
startCallRecording: (callId: string) => Promise<ApiResp | CallRecordingState>;
|
||||||
stopCallRecording: (callId: string) => Promise<ApiResp | RecordingState>;
|
stopCallRecording: (callId: string) => Promise<ApiResp | CallRecordingState>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ClientCalls = (superclass: any) => class extends superclass {
|
const ClientCalls = (superclass: any) => class extends superclass {
|
||||||
@@ -52,7 +48,7 @@ const ClientCalls = (superclass: any) => class extends superclass {
|
|||||||
return this.doFetch(
|
return this.doFetch(
|
||||||
`${this.getCallsRoute()}/config`,
|
`${this.getCallsRoute()}/config`,
|
||||||
{method: 'get'},
|
{method: 'get'},
|
||||||
) as ServerCallsConfig;
|
) as CallsConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
enableChannelCalls = async (channelId: string, enable: boolean) => {
|
enableChannelCalls = async (channelId: string, enable: boolean) => {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import CompassIcon from '@components/compass_icon';
|
|||||||
import Emoji from '@components/emoji';
|
import Emoji from '@components/emoji';
|
||||||
import ProfilePicture from '@components/profile_picture';
|
import ProfilePicture from '@components/profile_picture';
|
||||||
|
|
||||||
import type {CallReactionEmoji} from '@calls/types/calls';
|
import type {EmojiData} from '@mmcalls/common/lib/types';
|
||||||
import type UserModel from '@typings/database/models/servers/user';
|
import type UserModel from '@typings/database/models/servers/user';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -18,7 +18,7 @@ type Props = {
|
|||||||
muted?: boolean;
|
muted?: boolean;
|
||||||
sharingScreen?: boolean;
|
sharingScreen?: boolean;
|
||||||
raisedHand?: boolean;
|
raisedHand?: boolean;
|
||||||
reaction?: CallReactionEmoji;
|
reaction?: EmojiData;
|
||||||
size?: 'm' | 'l';
|
size?: 'm' | 'l';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
import {RTCPeer} from '@mmcalls/common/lib';
|
||||||
// @ts-ignore
|
import {deflate} from 'pako';
|
||||||
import {deflate} from 'pako/lib/deflate.js';
|
|
||||||
import {DeviceEventEmitter, EmitterSubscription} from 'react-native';
|
import {DeviceEventEmitter, EmitterSubscription} from 'react-native';
|
||||||
import InCallManager from 'react-native-incall-manager';
|
import InCallManager from 'react-native-incall-manager';
|
||||||
import {
|
import {
|
||||||
MediaStream,
|
MediaStream,
|
||||||
MediaStreamTrack,
|
MediaStreamTrack,
|
||||||
mediaDevices,
|
mediaDevices,
|
||||||
|
RTCPeerConnection,
|
||||||
} from 'react-native-webrtc';
|
} from 'react-native-webrtc';
|
||||||
|
|
||||||
import RTCPeer from '@calls/rtcpeer';
|
|
||||||
import {setSpeakerPhone} from '@calls/state';
|
import {setSpeakerPhone} from '@calls/state';
|
||||||
import {getICEServersConfigs} from '@calls/utils';
|
import {getICEServersConfigs} from '@calls/utils';
|
||||||
import {WebsocketEvents} from '@constants';
|
import {WebsocketEvents} from '@constants';
|
||||||
@@ -22,7 +21,9 @@ import {logError, logDebug, logWarning} from '@utils/log';
|
|||||||
|
|
||||||
import {WebSocketClient, wsReconnectionTimeoutErr} from './websocket_client';
|
import {WebSocketClient, wsReconnectionTimeoutErr} from './websocket_client';
|
||||||
|
|
||||||
import type {CallReactionEmoji, CallsConnection} from '@calls/types/calls';
|
import type {CallsConnection} from '@calls/types/calls';
|
||||||
|
import type {RTCPeerOpts} from '@mmcalls/common/lib/rtc_peer';
|
||||||
|
import type {EmojiData} from '@mmcalls/common/lib/types';
|
||||||
|
|
||||||
const peerConnectTimeout = 5000;
|
const peerConnectTimeout = 5000;
|
||||||
|
|
||||||
@@ -164,7 +165,7 @@ export async function newConnection(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const sendReaction = (emoji: CallReactionEmoji) => {
|
const sendReaction = (emoji: EmojiData) => {
|
||||||
if (ws) {
|
if (ws) {
|
||||||
ws.send('react', {
|
ws.send('react', {
|
||||||
data: JSON.stringify(emoji),
|
data: JSON.stringify(emoji),
|
||||||
@@ -204,7 +205,14 @@ export async function newConnection(
|
|||||||
InCallManager.start({media: 'video'});
|
InCallManager.start({media: 'video'});
|
||||||
setSpeakerPhone(true);
|
setSpeakerPhone(true);
|
||||||
|
|
||||||
peer = new RTCPeer({iceServers: iceConfigs || []});
|
const opts: RTCPeerOpts = {
|
||||||
|
logDebug,
|
||||||
|
webrtc: {
|
||||||
|
MediaStream,
|
||||||
|
RTCPeerConnection,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
peer = new RTCPeer({iceServers: iceConfigs || []}, opts);
|
||||||
|
|
||||||
peer.on('offer', (sdp) => {
|
peer.on('offer', (sdp) => {
|
||||||
logDebug(`local offer, sending: ${JSON.stringify(sdp)}`);
|
logDebug(`local offer, sending: ${JSON.stringify(sdp)}`);
|
||||||
|
|||||||
@@ -1,210 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
||||||
|
|
||||||
import {EventEmitter} from 'events';
|
|
||||||
|
|
||||||
import {
|
|
||||||
MediaStream,
|
|
||||||
MediaStreamTrack,
|
|
||||||
RTCIceCandidate,
|
|
||||||
RTCPeerConnection,
|
|
||||||
RTCPeerConnectionIceEvent,
|
|
||||||
RTCRtpSender,
|
|
||||||
RTCSessionDescription,
|
|
||||||
} from 'react-native-webrtc';
|
|
||||||
|
|
||||||
import {logDebug, logError} from '@utils/log';
|
|
||||||
|
|
||||||
import type {RTCPeerConfig} from './types';
|
|
||||||
import type RTCTrackEvent from 'react-native-webrtc/lib/typescript/RTCTrackEvent';
|
|
||||||
|
|
||||||
const rtcConnFailedErr = new Error('rtc connection failed');
|
|
||||||
|
|
||||||
export default class RTCPeer extends EventEmitter {
|
|
||||||
private pc: RTCPeerConnection | null;
|
|
||||||
private readonly senders: { [key: string]: RTCRtpSender };
|
|
||||||
private candidates: RTCIceCandidate[] = [];
|
|
||||||
private makingOffer = false;
|
|
||||||
|
|
||||||
public connected: boolean;
|
|
||||||
|
|
||||||
constructor(config: RTCPeerConfig) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
// We keep a map of track IDs -> RTP sender so that we can easily
|
|
||||||
// replace tracks when muting/unmuting.
|
|
||||||
this.senders = {};
|
|
||||||
|
|
||||||
this.pc = new RTCPeerConnection(config);
|
|
||||||
this.pc.onnegotiationneeded = () => this.onNegotiationNeeded();
|
|
||||||
this.pc.onicecandidate = (ev) => this.onICECandidate(ev);
|
|
||||||
this.pc.oniceconnectionstatechange = () => this.onICEConnectionStateChange();
|
|
||||||
this.pc.onconnectionstatechange = () => this.onConnectionStateChange();
|
|
||||||
this.pc.ontrack = (ev) => this.onTrack(ev);
|
|
||||||
|
|
||||||
this.connected = false;
|
|
||||||
|
|
||||||
// We create a data channel for two reasons:
|
|
||||||
// - Initiate a connection without preemptively adding audio/video tracks.
|
|
||||||
// - Use this communication channel for further negotiation (to be implemented).
|
|
||||||
this.pc.createDataChannel('calls-dc');
|
|
||||||
}
|
|
||||||
|
|
||||||
private onICECandidate(ev: RTCPeerConnectionIceEvent) {
|
|
||||||
if (ev.candidate) {
|
|
||||||
this.emit('candidate', ev.candidate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private onConnectionStateChange() {
|
|
||||||
switch (this.pc?.connectionState) {
|
|
||||||
case 'connected':
|
|
||||||
this.connected = true;
|
|
||||||
break;
|
|
||||||
case 'failed':
|
|
||||||
this.emit('close', rtcConnFailedErr);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private onICEConnectionStateChange() {
|
|
||||||
switch (this.pc?.iceConnectionState) {
|
|
||||||
case 'connected':
|
|
||||||
this.emit('connect');
|
|
||||||
break;
|
|
||||||
case 'failed':
|
|
||||||
this.emit('close', rtcConnFailedErr);
|
|
||||||
break;
|
|
||||||
case 'closed':
|
|
||||||
this.emit('close');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async onNegotiationNeeded() {
|
|
||||||
try {
|
|
||||||
this.makingOffer = true;
|
|
||||||
await this.pc?.setLocalDescription();
|
|
||||||
this.emit('offer', this.pc?.localDescription);
|
|
||||||
} catch (err) {
|
|
||||||
this.emit('error', err);
|
|
||||||
} finally {
|
|
||||||
this.makingOffer = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private onTrack(ev: RTCTrackEvent) {
|
|
||||||
if (ev.streams.length === 0) {
|
|
||||||
this.emit('stream', new MediaStream([ev.track]));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.emit('stream', ev.streams[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async signal(data: string) {
|
|
||||||
if (!this.pc) {
|
|
||||||
throw new Error('peer has been destroyed');
|
|
||||||
}
|
|
||||||
|
|
||||||
const msg = JSON.parse(data);
|
|
||||||
|
|
||||||
if (msg.type === 'offer' && (this.makingOffer || this.pc?.signalingState !== 'stable')) {
|
|
||||||
logDebug('signaling conflict, we are polite, proceeding...');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
switch (msg.type) {
|
|
||||||
case 'candidate':
|
|
||||||
// It's possible that ICE candidates are received moments before
|
|
||||||
// we set the initial remote description which would cause an
|
|
||||||
// error. In such case we queue them up to be added later.
|
|
||||||
if (this.pc.remoteDescription && this.pc.remoteDescription.type) {
|
|
||||||
this.pc.addIceCandidate(msg.candidate).catch((err) => {
|
|
||||||
logError('failed to add candidate', err);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
logDebug('received ice candidate before remote description, queuing...');
|
|
||||||
this.candidates.push(msg.candidate);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'offer':
|
|
||||||
await this.pc.setRemoteDescription(new RTCSessionDescription(msg));
|
|
||||||
await this.pc.setLocalDescription();
|
|
||||||
this.emit('answer', this.pc.localDescription);
|
|
||||||
break;
|
|
||||||
case 'answer':
|
|
||||||
await this.pc.setRemoteDescription(msg);
|
|
||||||
for (const candidate of this.candidates) {
|
|
||||||
logDebug('adding queued ice candidate');
|
|
||||||
this.pc.addIceCandidate(candidate).catch((err) => {
|
|
||||||
logError('failed to add candidate', err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.emit('error', Error('invalid signaling data received'));
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
this.emit('error', err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async addTrack(track: MediaStreamTrack, stream?: MediaStream) {
|
|
||||||
if (!this.pc) {
|
|
||||||
throw new Error('peer has been destroyed');
|
|
||||||
}
|
|
||||||
const sender = await this.pc.addTrack(track, stream!);
|
|
||||||
if (sender) {
|
|
||||||
this.senders[track.id] = sender;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public addStream(stream: MediaStream) {
|
|
||||||
stream.getTracks().forEach((track) => {
|
|
||||||
this.addTrack(track, stream);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public replaceTrack(oldTrackID: string, newTrack: MediaStreamTrack | null) {
|
|
||||||
const sender = this.senders[oldTrackID];
|
|
||||||
if (!sender) {
|
|
||||||
throw new Error('sender for track not found');
|
|
||||||
}
|
|
||||||
if (newTrack && newTrack.id !== oldTrackID) {
|
|
||||||
delete this.senders[oldTrackID];
|
|
||||||
this.senders[newTrack.id] = sender;
|
|
||||||
}
|
|
||||||
sender.replaceTrack(newTrack);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getStats() {
|
|
||||||
if (!this.pc) {
|
|
||||||
throw new Error('peer has been destroyed');
|
|
||||||
}
|
|
||||||
return this.pc.getStats();
|
|
||||||
}
|
|
||||||
|
|
||||||
public destroy() {
|
|
||||||
if (!this.pc) {
|
|
||||||
throw new Error('peer has been destroyed already');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.removeAllListeners('candidate');
|
|
||||||
this.removeAllListeners('connect');
|
|
||||||
this.removeAllListeners('error');
|
|
||||||
this.removeAllListeners('close');
|
|
||||||
this.removeAllListeners('offer');
|
|
||||||
this.removeAllListeners('answer');
|
|
||||||
this.removeAllListeners('stream');
|
|
||||||
this.pc.onnegotiationneeded = null;
|
|
||||||
this.pc.onicecandidate = null;
|
|
||||||
this.pc.oniceconnectionstatechange = null;
|
|
||||||
this.pc.onconnectionstatechange = null;
|
|
||||||
this.pc.ontrack = null;
|
|
||||||
this.pc.close();
|
|
||||||
this.pc = null;
|
|
||||||
this.connected = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
import type {RTCIceServer} from 'react-native-webrtc';
|
|
||||||
|
|
||||||
export type RTCPeerConfig = {
|
|
||||||
iceServers: RTCIceServer[];
|
|
||||||
}
|
|
||||||
@@ -40,8 +40,6 @@ import {
|
|||||||
setPluginEnabled,
|
setPluginEnabled,
|
||||||
setUserVoiceOn,
|
setUserVoiceOn,
|
||||||
} from '@calls/state/actions';
|
} from '@calls/state/actions';
|
||||||
import {License} from '@constants';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Call,
|
Call,
|
||||||
CallsState,
|
CallsState,
|
||||||
@@ -51,8 +49,10 @@ import {
|
|||||||
DefaultCurrentCall,
|
DefaultCurrentCall,
|
||||||
DefaultGlobalCallsState,
|
DefaultGlobalCallsState,
|
||||||
GlobalCallsState,
|
GlobalCallsState,
|
||||||
RecordingState,
|
} from '@calls/types/calls';
|
||||||
} from '../types/calls';
|
import {License} from '@constants';
|
||||||
|
|
||||||
|
import type {CallRecordingState} from '@mmcalls/common/lib/types';
|
||||||
|
|
||||||
jest.mock('@calls/alerts');
|
jest.mock('@calls/alerts');
|
||||||
|
|
||||||
@@ -797,6 +797,7 @@ describe('useCallsState', () => {
|
|||||||
|
|
||||||
it('config', () => {
|
it('config', () => {
|
||||||
const newConfig = {
|
const newConfig = {
|
||||||
|
...DefaultCallsConfig,
|
||||||
ICEServers: [],
|
ICEServers: [],
|
||||||
ICEServersConfigs: [
|
ICEServersConfigs: [
|
||||||
{
|
{
|
||||||
@@ -914,7 +915,7 @@ describe('useCallsState', () => {
|
|||||||
myUserId: 'myUserId',
|
myUserId: 'myUserId',
|
||||||
...call1,
|
...call1,
|
||||||
};
|
};
|
||||||
const recState: RecordingState = {
|
const recState: CallRecordingState = {
|
||||||
init_at: 123,
|
init_at: 123,
|
||||||
start_at: 231,
|
start_at: 231,
|
||||||
end_at: 345,
|
end_at: 345,
|
||||||
|
|||||||
@@ -16,17 +16,17 @@ import {
|
|||||||
} from '@calls/state';
|
} from '@calls/state';
|
||||||
import {
|
import {
|
||||||
Call,
|
Call,
|
||||||
CallReaction,
|
CallsConfigState,
|
||||||
CallsConfig,
|
|
||||||
ChannelsWithCalls,
|
ChannelsWithCalls,
|
||||||
CurrentCall,
|
CurrentCall,
|
||||||
DefaultCall,
|
DefaultCall,
|
||||||
DefaultCurrentCall,
|
DefaultCurrentCall,
|
||||||
ReactionStreamEmoji,
|
ReactionStreamEmoji,
|
||||||
RecordingState,
|
|
||||||
} from '@calls/types/calls';
|
} from '@calls/types/calls';
|
||||||
import {REACTION_LIMIT, REACTION_TIMEOUT} from '@constants/calls';
|
import {REACTION_LIMIT, REACTION_TIMEOUT} from '@constants/calls';
|
||||||
|
|
||||||
|
import type {CallRecordingState, UserReactionData} from '@mmcalls/common/lib/types';
|
||||||
|
|
||||||
export const setCalls = (serverUrl: string, myUserId: string, calls: Dictionary<Call>, enabled: Dictionary<boolean>) => {
|
export const setCalls = (serverUrl: string, myUserId: string, calls: Dictionary<Call>, enabled: Dictionary<boolean>) => {
|
||||||
const channelsWithCalls = Object.keys(calls).reduce(
|
const channelsWithCalls = Object.keys(calls).reduce(
|
||||||
(accum, next) => {
|
(accum, next) => {
|
||||||
@@ -390,7 +390,7 @@ export const setSpeakerPhone = (speakerphoneOn: boolean) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setConfig = (serverUrl: string, config: Partial<CallsConfig>) => {
|
export const setConfig = (serverUrl: string, config: Partial<CallsConfigState>) => {
|
||||||
const callsConfig = getCallsConfig(serverUrl);
|
const callsConfig = getCallsConfig(serverUrl);
|
||||||
setCallsConfig(serverUrl, {...callsConfig, ...config});
|
setCallsConfig(serverUrl, {...callsConfig, ...config});
|
||||||
};
|
};
|
||||||
@@ -423,7 +423,7 @@ export const setMicPermissionsErrorDismissed = () => {
|
|||||||
setCurrentCall(nextCurrentCall);
|
setCurrentCall(nextCurrentCall);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const userReacted = (serverUrl: string, channelId: string, reaction: CallReaction) => {
|
export const userReacted = (serverUrl: string, channelId: string, reaction: UserReactionData) => {
|
||||||
// Note: Simplification for performance:
|
// Note: Simplification for performance:
|
||||||
// If you are not in the call with the reaction, ignore it. There could be many calls ongoing in your
|
// If you are not in the call with the reaction, ignore it. There could be many calls ongoing in your
|
||||||
// servers, do we want to be tracking reactions and setting timeouts for all those calls? No.
|
// servers, do we want to be tracking reactions and setting timeouts for all those calls? No.
|
||||||
@@ -474,7 +474,7 @@ export const userReacted = (serverUrl: string, channelId: string, reaction: Call
|
|||||||
}, REACTION_TIMEOUT);
|
}, REACTION_TIMEOUT);
|
||||||
};
|
};
|
||||||
|
|
||||||
const userReactionTimeout = (serverUrl: string, channelId: string, reaction: CallReaction) => {
|
const userReactionTimeout = (serverUrl: string, channelId: string, reaction: UserReactionData) => {
|
||||||
const currentCall = getCurrentCall();
|
const currentCall = getCurrentCall();
|
||||||
if (currentCall?.channelId !== channelId) {
|
if (currentCall?.channelId !== channelId) {
|
||||||
return;
|
return;
|
||||||
@@ -498,7 +498,7 @@ const userReactionTimeout = (serverUrl: string, channelId: string, reaction: Cal
|
|||||||
setCurrentCall(nextCurrentCall);
|
setCurrentCall(nextCurrentCall);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setRecordingState = (serverUrl: string, channelId: string, recState: RecordingState) => {
|
export const setRecordingState = (serverUrl: string, channelId: string, recState: CallRecordingState) => {
|
||||||
const callsState = getCallsState(serverUrl);
|
const callsState = getCallsState(serverUrl);
|
||||||
if (!callsState.calls[channelId]) {
|
if (!callsState.calls[channelId]) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
import {useEffect, useState} from 'react';
|
import {useEffect, useState} from 'react';
|
||||||
import {BehaviorSubject} from 'rxjs';
|
import {BehaviorSubject} from 'rxjs';
|
||||||
|
|
||||||
import {CallsConfig, DefaultCallsConfig} from '@calls/types/calls';
|
import {CallsConfigState, DefaultCallsConfig} from '@calls/types/calls';
|
||||||
|
|
||||||
const callsConfigSubjects: Dictionary<BehaviorSubject<CallsConfig>> = {};
|
const callsConfigSubjects: Dictionary<BehaviorSubject<CallsConfigState>> = {};
|
||||||
|
|
||||||
const getCallsConfigSubject = (serverUrl: string) => {
|
const getCallsConfigSubject = (serverUrl: string) => {
|
||||||
if (!callsConfigSubjects[serverUrl]) {
|
if (!callsConfigSubjects[serverUrl]) {
|
||||||
@@ -20,7 +20,7 @@ export const getCallsConfig = (serverUrl: string) => {
|
|||||||
return getCallsConfigSubject(serverUrl).value;
|
return getCallsConfigSubject(serverUrl).value;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setCallsConfig = (serverUrl: string, callsConfig: CallsConfig) => {
|
export const setCallsConfig = (serverUrl: string, callsConfig: CallsConfigState) => {
|
||||||
getCallsConfigSubject(serverUrl).next(callsConfig);
|
getCallsConfigSubject(serverUrl).next(callsConfig);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import type {CallRecordingState, CallsConfig, EmojiData, UserReactionData} from '@mmcalls/common/lib/types';
|
||||||
import type UserModel from '@typings/database/models/servers/user';
|
import type UserModel from '@typings/database/models/servers/user';
|
||||||
import type {RTCIceServer} from 'react-native-webrtc';
|
|
||||||
|
|
||||||
export type GlobalCallsState = {
|
export type GlobalCallsState = {
|
||||||
micPermissionsGranted: boolean;
|
micPermissionsGranted: boolean;
|
||||||
@@ -31,7 +31,7 @@ export type Call = {
|
|||||||
screenOn: string;
|
screenOn: string;
|
||||||
threadId: string;
|
threadId: string;
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
recState?: RecordingState;
|
recState?: CallRecordingState;
|
||||||
hostId: string;
|
hostId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ export type CallParticipant = {
|
|||||||
muted: boolean;
|
muted: boolean;
|
||||||
raisedHand: number;
|
raisedHand: number;
|
||||||
userModel?: UserModel;
|
userModel?: UserModel;
|
||||||
reaction?: CallReaction;
|
reaction?: UserReactionData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ChannelsWithCalls = Dictionary<boolean>;
|
export type ChannelsWithCalls = Dictionary<boolean>;
|
||||||
@@ -100,7 +100,7 @@ export type ServerCallState = {
|
|||||||
screen_sharing_id: string;
|
screen_sharing_id: string;
|
||||||
owner_id: string;
|
owner_id: string;
|
||||||
host_id: string;
|
host_id: string;
|
||||||
recording: RecordingState;
|
recording: CallRecordingState;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CallsConnection = {
|
export type CallsConnection = {
|
||||||
@@ -111,26 +111,16 @@ export type CallsConnection = {
|
|||||||
raiseHand: () => void;
|
raiseHand: () => void;
|
||||||
unraiseHand: () => void;
|
unraiseHand: () => void;
|
||||||
initializeVoiceTrack: () => void;
|
initializeVoiceTrack: () => void;
|
||||||
sendReaction: (emoji: CallReactionEmoji) => void;
|
sendReaction: (emoji: EmojiData) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ServerCallsConfig = {
|
export type CallsConfigState = CallsConfig & {
|
||||||
ICEServers?: string[]; // deprecated
|
|
||||||
ICEServersConfigs: RTCIceServer[];
|
|
||||||
AllowEnableCalls: boolean;
|
AllowEnableCalls: boolean;
|
||||||
DefaultEnabled: boolean;
|
|
||||||
NeedsTURNCredentials: boolean;
|
|
||||||
sku_short_name: string;
|
|
||||||
MaxCallParticipants: number;
|
|
||||||
EnableRecordings: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type CallsConfig = ServerCallsConfig & {
|
|
||||||
pluginEnabled: boolean;
|
pluginEnabled: boolean;
|
||||||
last_retrieved_at: number;
|
last_retrieved_at: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DefaultCallsConfig: CallsConfig = {
|
export const DefaultCallsConfig: CallsConfigState = {
|
||||||
pluginEnabled: false,
|
pluginEnabled: false,
|
||||||
ICEServers: [], // deprecated
|
ICEServers: [], // deprecated
|
||||||
ICEServersConfigs: [],
|
ICEServersConfigs: [],
|
||||||
@@ -141,6 +131,8 @@ export const DefaultCallsConfig: CallsConfig = {
|
|||||||
sku_short_name: '',
|
sku_short_name: '',
|
||||||
MaxCallParticipants: 0,
|
MaxCallParticipants: 0,
|
||||||
EnableRecordings: false,
|
EnableRecordings: false,
|
||||||
|
MaxRecordingDuration: 60,
|
||||||
|
AllowScreenSharing: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ApiResp = {
|
export type ApiResp = {
|
||||||
@@ -149,18 +141,6 @@ export type ApiResp = {
|
|||||||
status_code: number;
|
status_code: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CallReactionEmoji = {
|
|
||||||
name: string;
|
|
||||||
skin?: string;
|
|
||||||
unified: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type CallReaction = {
|
|
||||||
user_id: string;
|
|
||||||
emoji: CallReactionEmoji;
|
|
||||||
timestamp: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ReactionStreamEmoji = {
|
export type ReactionStreamEmoji = {
|
||||||
name: string;
|
name: string;
|
||||||
latestTimestamp: number;
|
latestTimestamp: number;
|
||||||
|
|||||||
@@ -3,15 +3,15 @@
|
|||||||
|
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
|
||||||
|
import {CallsConfigState, DefaultCallsConfig} from '@calls/types/calls';
|
||||||
import {License} from '@constants';
|
import {License} from '@constants';
|
||||||
|
|
||||||
import {getICEServersConfigs} from './utils';
|
import {getICEServersConfigs} from './utils';
|
||||||
|
|
||||||
import type {CallsConfig} from '@calls/types/calls';
|
|
||||||
|
|
||||||
describe('getICEServersConfigs', () => {
|
describe('getICEServersConfigs', () => {
|
||||||
it('backwards compatible case, no ICEServersConfigs present', () => {
|
it('backwards compatible case, no ICEServersConfigs present', () => {
|
||||||
const config: CallsConfig = {
|
const config: CallsConfigState = {
|
||||||
|
...DefaultCallsConfig,
|
||||||
pluginEnabled: true,
|
pluginEnabled: true,
|
||||||
ICEServers: ['stun:stun.example.com:3478'],
|
ICEServers: ['stun:stun.example.com:3478'],
|
||||||
ICEServersConfigs: [],
|
ICEServersConfigs: [],
|
||||||
@@ -33,7 +33,8 @@ describe('getICEServersConfigs', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('ICEServersConfigs set', () => {
|
it('ICEServersConfigs set', () => {
|
||||||
const config: CallsConfig = {
|
const config: CallsConfigState = {
|
||||||
|
...DefaultCallsConfig,
|
||||||
pluginEnabled: true,
|
pluginEnabled: true,
|
||||||
ICEServersConfigs: [
|
ICEServersConfigs: [
|
||||||
{
|
{
|
||||||
@@ -64,7 +65,8 @@ describe('getICEServersConfigs', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Both ICEServers and ICEServersConfigs set', () => {
|
it('Both ICEServers and ICEServersConfigs set', () => {
|
||||||
const config: CallsConfig = {
|
const config: CallsConfigState = {
|
||||||
|
...DefaultCallsConfig,
|
||||||
pluginEnabled: true,
|
pluginEnabled: true,
|
||||||
ICEServers: ['stun:stuna.example.com:3478'],
|
ICEServers: ['stun:stuna.example.com:3478'],
|
||||||
ICEServersConfigs: [
|
ICEServersConfigs: [
|
||||||
|
|||||||
@@ -8,9 +8,11 @@ import Calls from '@constants/calls';
|
|||||||
import {isMinimumServerVersion} from '@utils/helpers';
|
import {isMinimumServerVersion} from '@utils/helpers';
|
||||||
import {displayUsername} from '@utils/user';
|
import {displayUsername} from '@utils/user';
|
||||||
|
|
||||||
import type {CallParticipant, ServerCallsConfig} from '@calls/types/calls';
|
import type {CallParticipant} from '@calls/types/calls';
|
||||||
|
import type {CallsConfig} from '@mmcalls/common/lib/types';
|
||||||
import type PostModel from '@typings/database/models/servers/post';
|
import type PostModel from '@typings/database/models/servers/post';
|
||||||
import type {IntlShape} from 'react-intl';
|
import type {IntlShape} from 'react-intl';
|
||||||
|
import type {RTCIceServer} from 'react-native-webrtc';
|
||||||
|
|
||||||
export function sortParticipants(teammateNameDisplay: string, participants?: Dictionary<CallParticipant>, presenterID?: string): CallParticipant[] {
|
export function sortParticipants(teammateNameDisplay: string, participants?: Dictionary<CallParticipant>, presenterID?: string): CallParticipant[] {
|
||||||
if (!participants) {
|
if (!participants) {
|
||||||
@@ -106,7 +108,7 @@ export function errorAlert(error: string, intl: IntlShape) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getICEServersConfigs(config: ServerCallsConfig) {
|
export function getICEServersConfigs(config: CallsConfig): RTCIceServer[] {
|
||||||
// if ICEServersConfigs is set, we can trust this to be complete and
|
// if ICEServersConfigs is set, we can trust this to be complete and
|
||||||
// coming from an updated API.
|
// coming from an updated API.
|
||||||
if (config.ICEServersConfigs && config.ICEServersConfigs.length > 0) {
|
if (config.ICEServersConfigs && config.ICEServersConfigs.length > 0) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||||
import withObservables from '@nozbe/with-observables';
|
import withObservables from '@nozbe/with-observables';
|
||||||
import {of as of$, Observable} from 'rxjs';
|
import {of as of$} from 'rxjs';
|
||||||
import {switchMap, combineLatestWith, distinctUntilChanged} from 'rxjs/operators';
|
import {switchMap, combineLatestWith, distinctUntilChanged} from 'rxjs/operators';
|
||||||
|
|
||||||
import {Preferences} from '@constants';
|
import {Preferences} from '@constants';
|
||||||
@@ -19,7 +19,6 @@ import CategoryBody from './category_body';
|
|||||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||||
import type CategoryModel from '@typings/database/models/servers/category';
|
import type CategoryModel from '@typings/database/models/servers/category';
|
||||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||||
import type MyChannelModel from '@typings/database/models/servers/my_channel';
|
|
||||||
import type PreferenceModel from '@typings/database/models/servers/preference';
|
import type PreferenceModel from '@typings/database/models/servers/preference';
|
||||||
|
|
||||||
type EnhanceProps = {
|
type EnhanceProps = {
|
||||||
@@ -31,7 +30,8 @@ type EnhanceProps = {
|
|||||||
|
|
||||||
const withUserId = withObservables([], ({database}: WithDatabaseArgs) => ({currentUserId: observeCurrentUserId(database)}));
|
const withUserId = withObservables([], ({database}: WithDatabaseArgs) => ({currentUserId: observeCurrentUserId(database)}));
|
||||||
|
|
||||||
const observeCategoryChannels = (category: CategoryModel, myChannels: Observable<MyChannelModel[]>) => {
|
const observeCategoryChannels = (category: CategoryModel) => {
|
||||||
|
const myChannels = category.myChannels.observeWithColumns(['last_post_at', 'is_unread']);
|
||||||
const channels = category.channels.observeWithColumns(['create_at', 'display_name']);
|
const channels = category.channels.observeWithColumns(['create_at', 'display_name']);
|
||||||
const manualSort = category.categoryChannelsBySortOrder.observeWithColumns(['sort_order']);
|
const manualSort = category.categoryChannelsBySortOrder.observeWithColumns(['sort_order']);
|
||||||
return myChannels.pipe(
|
return myChannels.pipe(
|
||||||
@@ -57,8 +57,7 @@ const observeCategoryChannels = (category: CategoryModel, myChannels: Observable
|
|||||||
};
|
};
|
||||||
|
|
||||||
const enhanced = withObservables([], ({category, currentUserId, database, isTablet, locale}: EnhanceProps) => {
|
const enhanced = withObservables([], ({category, currentUserId, database, isTablet, locale}: EnhanceProps) => {
|
||||||
const categoryMyChannels = category.myChannels.observeWithColumns(['last_post_at', 'is_unread']);
|
const channelsWithMyChannel = observeCategoryChannels(category);
|
||||||
const channelsWithMyChannel = observeCategoryChannels(category, categoryMyChannels);
|
|
||||||
const currentChannelId = isTablet ? observeCurrentChannelId(database) : of$('');
|
const currentChannelId = isTablet ? observeCurrentChannelId(database) : of$('');
|
||||||
const lastUnreadId = isTablet ? observeLastUnreadChannelId(database) : of$(undefined);
|
const lastUnreadId = isTablet ? observeLastUnreadChannelId(database) : of$(undefined);
|
||||||
|
|
||||||
@@ -78,9 +77,9 @@ const enhanced = withObservables([], ({category, currentUserId, database, isTabl
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const notifyPropsPerChannel = categoryMyChannels.pipe(
|
const notifyPropsPerChannel = channelsWithMyChannel.pipe(
|
||||||
// eslint-disable-next-line max-nested-callbacks
|
// eslint-disable-next-line max-nested-callbacks
|
||||||
switchMap((mc) => observeNotifyPropsByChannels(database, mc)),
|
switchMap((cwms) => observeNotifyPropsByChannels(database, cwms.map((c) => c.myChannel))),
|
||||||
);
|
);
|
||||||
|
|
||||||
const hiddenDmPrefs = queryPreferencesByCategoryAndName(database, Preferences.CATEGORIES.DIRECT_CHANNEL_SHOW, undefined, 'false').
|
const hiddenDmPrefs = queryPreferencesByCategoryAndName(database, Preferences.CATEGORIES.DIRECT_CHANNEL_SHOW, undefined, 'false').
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||||
import withObservables from '@nozbe/with-observables';
|
import withObservables from '@nozbe/with-observables';
|
||||||
import {combineLatest, of as of$} from 'rxjs';
|
import {combineLatest, of as of$} from 'rxjs';
|
||||||
import {distinctUntilChanged, switchMap} from 'rxjs/operators';
|
import {switchMap} from 'rxjs/operators';
|
||||||
|
|
||||||
import {Permissions} from '@constants';
|
import {Permissions} from '@constants';
|
||||||
import {observePermissionForTeam} from '@queries/servers/role';
|
import {observePermissionForTeam} from '@queries/servers/role';
|
||||||
@@ -25,7 +25,6 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
|||||||
|
|
||||||
const canJoinChannels = combineLatest([currentUser, team]).pipe(
|
const canJoinChannels = combineLatest([currentUser, team]).pipe(
|
||||||
switchMap(([u, t]) => observePermissionForTeam(database, t, u, Permissions.JOIN_PUBLIC_CHANNELS, true)),
|
switchMap(([u, t]) => observePermissionForTeam(database, t, u, Permissions.JOIN_PUBLIC_CHANNELS, true)),
|
||||||
distinctUntilChanged(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const canCreatePublicChannels = combineLatest([currentUser, team]).pipe(
|
const canCreatePublicChannels = combineLatest([currentUser, team]).pipe(
|
||||||
@@ -38,7 +37,6 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
|||||||
|
|
||||||
const canCreateChannels = combineLatest([canCreatePublicChannels, canCreatePrivateChannels]).pipe(
|
const canCreateChannels = combineLatest([canCreatePublicChannels, canCreatePrivateChannels]).pipe(
|
||||||
switchMap(([open, priv]) => of$(open || priv)),
|
switchMap(([open, priv]) => of$(open || priv)),
|
||||||
distinctUntilChanged(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const canAddUserToTeam = combineLatest([currentUser, team]).pipe(
|
const canAddUserToTeam = combineLatest([currentUser, team]).pipe(
|
||||||
@@ -50,11 +48,9 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
|||||||
canJoinChannels,
|
canJoinChannels,
|
||||||
canInvitePeople: combineLatest([enableOpenServer, canAddUserToTeam]).pipe(
|
canInvitePeople: combineLatest([enableOpenServer, canAddUserToTeam]).pipe(
|
||||||
switchMap(([openServer, addUser]) => of$(openServer && addUser)),
|
switchMap(([openServer, addUser]) => of$(openServer && addUser)),
|
||||||
distinctUntilChanged(),
|
|
||||||
),
|
),
|
||||||
displayName: team.pipe(
|
displayName: team.pipe(
|
||||||
switchMap((t) => of$(t?.displayName)),
|
switchMap((t) => of$(t?.displayName)),
|
||||||
distinctUntilChanged(),
|
|
||||||
),
|
),
|
||||||
pushProxyStatus: observePushVerificationStatus(database),
|
pushProxyStatus: observePushVerificationStatus(database),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ describe('components/categories_list', () => {
|
|||||||
it('should render', () => {
|
it('should render', () => {
|
||||||
const wrapper = renderWithEverything(
|
const wrapper = renderWithEverything(
|
||||||
<CategoriesList
|
<CategoriesList
|
||||||
moreThanOneTeam={false}
|
teamsCount={1}
|
||||||
hasChannels={true}
|
channelsCount={1}
|
||||||
/>,
|
/>,
|
||||||
{database},
|
{database},
|
||||||
);
|
);
|
||||||
@@ -46,8 +46,8 @@ describe('components/categories_list', () => {
|
|||||||
const wrapper = renderWithEverything(
|
const wrapper = renderWithEverything(
|
||||||
<CategoriesList
|
<CategoriesList
|
||||||
isCRTEnabled={true}
|
isCRTEnabled={true}
|
||||||
moreThanOneTeam={false}
|
teamsCount={1}
|
||||||
hasChannels={true}
|
channelsCount={1}
|
||||||
/>,
|
/>,
|
||||||
{database},
|
{database},
|
||||||
);
|
);
|
||||||
@@ -67,8 +67,8 @@ describe('components/categories_list', () => {
|
|||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
const wrapper = renderWithEverything(
|
const wrapper = renderWithEverything(
|
||||||
<CategoriesList
|
<CategoriesList
|
||||||
moreThanOneTeam={false}
|
teamsCount={0}
|
||||||
hasChannels={true}
|
channelsCount={1}
|
||||||
/>,
|
/>,
|
||||||
{database},
|
{database},
|
||||||
);
|
);
|
||||||
@@ -89,8 +89,8 @@ describe('components/categories_list', () => {
|
|||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
const wrapper = renderWithEverything(
|
const wrapper = renderWithEverything(
|
||||||
<CategoriesList
|
<CategoriesList
|
||||||
moreThanOneTeam={true}
|
teamsCount={1}
|
||||||
hasChannels={false}
|
channelsCount={0}
|
||||||
/>,
|
/>,
|
||||||
{database},
|
{database},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -27,28 +27,28 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
type ChannelListProps = {
|
type ChannelListProps = {
|
||||||
hasChannels: boolean;
|
channelsCount: number;
|
||||||
iconPad?: boolean;
|
iconPad?: boolean;
|
||||||
isCRTEnabled?: boolean;
|
isCRTEnabled?: boolean;
|
||||||
moreThanOneTeam: boolean;
|
teamsCount: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTabletWidth = (moreThanOneTeam: boolean) => {
|
const getTabletWidth = (teamsCount: number) => {
|
||||||
return TABLET_SIDEBAR_WIDTH - (moreThanOneTeam ? TEAM_SIDEBAR_WIDTH : 0);
|
return TABLET_SIDEBAR_WIDTH - (teamsCount > 1 ? TEAM_SIDEBAR_WIDTH : 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
const CategoriesList = ({hasChannels, iconPad, isCRTEnabled, moreThanOneTeam}: ChannelListProps) => {
|
const CategoriesList = ({channelsCount, iconPad, isCRTEnabled, teamsCount}: ChannelListProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const styles = getStyleSheet(theme);
|
const styles = getStyleSheet(theme);
|
||||||
const {width} = useWindowDimensions();
|
const {width} = useWindowDimensions();
|
||||||
const isTablet = useIsTablet();
|
const isTablet = useIsTablet();
|
||||||
const tabletWidth = useSharedValue(isTablet ? getTabletWidth(moreThanOneTeam) : 0);
|
const tabletWidth = useSharedValue(isTablet ? getTabletWidth(teamsCount) : 0);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isTablet) {
|
if (isTablet) {
|
||||||
tabletWidth.value = getTabletWidth(moreThanOneTeam);
|
tabletWidth.value = getTabletWidth(teamsCount);
|
||||||
}
|
}
|
||||||
}, [isTablet && moreThanOneTeam]);
|
}, [isTablet && teamsCount]);
|
||||||
|
|
||||||
const tabletStyle = useAnimatedStyle(() => {
|
const tabletStyle = useAnimatedStyle(() => {
|
||||||
if (!isTablet) {
|
if (!isTablet) {
|
||||||
@@ -61,7 +61,7 @@ const CategoriesList = ({hasChannels, iconPad, isCRTEnabled, moreThanOneTeam}: C
|
|||||||
}, [isTablet, width]);
|
}, [isTablet, width]);
|
||||||
|
|
||||||
const content = useMemo(() => {
|
const content = useMemo(() => {
|
||||||
if (!hasChannels) {
|
if (channelsCount < 1) {
|
||||||
return (<LoadChannelsError/>);
|
return (<LoadChannelsError/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,10 +29,9 @@ import Servers from './servers';
|
|||||||
import type {LaunchType} from '@typings/launch';
|
import type {LaunchType} from '@typings/launch';
|
||||||
|
|
||||||
type ChannelProps = {
|
type ChannelProps = {
|
||||||
hasChannels: boolean;
|
channelsCount: number;
|
||||||
isCRTEnabled: boolean;
|
isCRTEnabled: boolean;
|
||||||
hasTeams: boolean;
|
teamsCount: number;
|
||||||
hasMoreThanOneTeam: boolean;
|
|
||||||
isLicensed: boolean;
|
isLicensed: boolean;
|
||||||
showToS: boolean;
|
showToS: boolean;
|
||||||
launchType: LaunchType;
|
launchType: LaunchType;
|
||||||
@@ -127,10 +126,10 @@ const ChannelListScreen = (props: ChannelProps) => {
|
|||||||
}, [theme, insets.top]);
|
}, [theme, insets.top]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!props.hasTeams) {
|
if (!props.teamsCount) {
|
||||||
resetToTeams();
|
resetToTeams();
|
||||||
}
|
}
|
||||||
}, [Boolean(props.hasTeams)]);
|
}, [Boolean(props.teamsCount)]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const back = BackHandler.addEventListener('hardwareBackPress', handleBackPress);
|
const back = BackHandler.addEventListener('hardwareBackPress', handleBackPress);
|
||||||
@@ -177,13 +176,13 @@ const ChannelListScreen = (props: ChannelProps) => {
|
|||||||
>
|
>
|
||||||
<TeamSidebar
|
<TeamSidebar
|
||||||
iconPad={canAddOtherServers}
|
iconPad={canAddOtherServers}
|
||||||
hasMoreThanOneTeam={props.hasMoreThanOneTeam}
|
teamsCount={props.teamsCount}
|
||||||
/>
|
/>
|
||||||
<CategoriesList
|
<CategoriesList
|
||||||
iconPad={canAddOtherServers && !props.hasMoreThanOneTeam}
|
iconPad={canAddOtherServers && props.teamsCount <= 1}
|
||||||
isCRTEnabled={props.isCRTEnabled}
|
isCRTEnabled={props.isCRTEnabled}
|
||||||
moreThanOneTeam={props.hasMoreThanOneTeam}
|
teamsCount={props.teamsCount}
|
||||||
hasChannels={props.hasChannels}
|
channelsCount={props.channelsCount}
|
||||||
/>
|
/>
|
||||||
{isTablet &&
|
{isTablet &&
|
||||||
<AdditionalTabletView/>
|
<AdditionalTabletView/>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||||
import withObservables from '@nozbe/with-observables';
|
import withObservables from '@nozbe/with-observables';
|
||||||
import {of as of$} from 'rxjs';
|
import {of as of$} from 'rxjs';
|
||||||
import {distinctUntilChanged, switchMap} from 'rxjs/operators';
|
import {switchMap} from 'rxjs/operators';
|
||||||
|
|
||||||
import {queryAllMyChannelsForTeam} from '@queries/servers/channel';
|
import {queryAllMyChannelsForTeam} from '@queries/servers/channel';
|
||||||
import {observeCurrentTeamId, observeLicense} from '@queries/servers/system';
|
import {observeCurrentTeamId, observeLicense} from '@queries/servers/system';
|
||||||
@@ -21,22 +21,11 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
|||||||
switchMap((lcs) => (lcs ? of$(lcs.IsLicensed === 'true') : of$(false))),
|
switchMap((lcs) => (lcs ? of$(lcs.IsLicensed === 'true') : of$(false))),
|
||||||
);
|
);
|
||||||
|
|
||||||
const teamsCount = queryMyTeams(database).observeCount(false);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isCRTEnabled: observeIsCRTEnabled(database),
|
isCRTEnabled: observeIsCRTEnabled(database),
|
||||||
hasTeams: teamsCount.pipe(
|
teamsCount: queryMyTeams(database).observeCount(false),
|
||||||
switchMap((v) => of$(v > 0)),
|
channelsCount: observeCurrentTeamId(database).pipe(
|
||||||
distinctUntilChanged(),
|
|
||||||
),
|
|
||||||
hasMoreThanOneTeam: teamsCount.pipe(
|
|
||||||
switchMap((v) => of$(v > 1)),
|
|
||||||
distinctUntilChanged(),
|
|
||||||
),
|
|
||||||
hasChannels: observeCurrentTeamId(database).pipe(
|
|
||||||
switchMap((id) => (id ? queryAllMyChannelsForTeam(database, id).observeCount(false) : of$(0))),
|
switchMap((id) => (id ? queryAllMyChannelsForTeam(database, id).observeCount(false) : of$(0))),
|
||||||
switchMap((v) => of$(v > 0)),
|
|
||||||
distinctUntilChanged(),
|
|
||||||
),
|
),
|
||||||
isLicensed,
|
isLicensed,
|
||||||
showToS: observeShowToS(database),
|
showToS: observeShowToS(database),
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
import {General, Preferences} from '@constants';
|
import {General, Preferences} from '@constants';
|
||||||
import {DMS_CATEGORY} from '@constants/categories';
|
import {DMS_CATEGORY} from '@constants/categories';
|
||||||
import {getPreferenceAsBool} from '@helpers/api/preference';
|
import {getPreferenceAsBool, getPreferenceValue} from '@helpers/api/preference';
|
||||||
import {isDMorGM} from '@utils/channel';
|
import {isDMorGM} from '@utils/channel';
|
||||||
import {getUserIdFromChannelName} from '@utils/user';
|
import {getUserIdFromChannelName} from '@utils/user';
|
||||||
|
|
||||||
@@ -42,18 +42,15 @@ export const filterAutoclosedDMs = (
|
|||||||
// Only autoclose DMs that haven't been assigned to a category
|
// Only autoclose DMs that haven't been assigned to a category
|
||||||
return channelsWithMyChannel;
|
return channelsWithMyChannel;
|
||||||
}
|
}
|
||||||
const prefMap = preferences.reduce((acc, v) => {
|
|
||||||
const existing = acc.get(v.name);
|
|
||||||
acc.set(v.name, Math.max((v.value as unknown as number) || 0, existing || 0));
|
|
||||||
return acc;
|
|
||||||
}, new Map<string, number>());
|
|
||||||
const getLastViewedAt = (cwm: ChannelWithMyChannel) => {
|
const getLastViewedAt = (cwm: ChannelWithMyChannel) => {
|
||||||
// The server only ever sets the last_viewed_at to the time of the last post in channel, so we may need
|
// The server only ever sets the last_viewed_at to the time of the last post in channel, so we may need
|
||||||
// to use the preferences added for the previous version of autoclosing DMs.
|
// to use the preferences added for the previous version of autoclosing DMs.
|
||||||
const id = cwm.channel.id;
|
const id = cwm.channel.id;
|
||||||
return Math.max(
|
return Math.max(
|
||||||
cwm.myChannel.lastViewedAt,
|
cwm.myChannel.lastViewedAt,
|
||||||
prefMap.get(id) || 0,
|
getPreferenceValue<number>(preferences, Preferences.CATEGORIES.CHANNEL_APPROXIMATE_VIEW_TIME, id, 0),
|
||||||
|
getPreferenceValue<number>(preferences, Preferences.CATEGORIES.CHANNEL_OPEN_TIME, id, 0),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -181,7 +178,7 @@ export const sortChannels = (sorting: CategorySorting, channelsWithMyChannel: Ch
|
|||||||
}).map((cwm) => cwm.channel);
|
}).map((cwm) => cwm.channel);
|
||||||
} else if (sorting === 'manual') {
|
} else if (sorting === 'manual') {
|
||||||
return channelsWithMyChannel.sort((cwmA, cwmB) => {
|
return channelsWithMyChannel.sort((cwmA, cwmB) => {
|
||||||
return cwmA.sortOrder - cwmB.sortOrder;
|
return cwmB.sortOrder - cwmA.sortOrder;
|
||||||
}).map((cwm) => cwm.channel);
|
}).map((cwm) => cwm.channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
|
|
||||||
import {isMinimumServerVersion} from './helpers';
|
import {isMinimumServerVersion} from './helpers';
|
||||||
|
|
||||||
export function hasReliableWebsocket(version?: string, reliableWebsocketsConfig?: string) {
|
export function hasReliableWebsocket(config: ClientConfig) {
|
||||||
if (version && isMinimumServerVersion(version, 6, 5)) {
|
if (isMinimumServerVersion(config.Version, 6, 5)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return reliableWebsocketsConfig === 'true';
|
return config.EnableReliableWebSockets === 'true';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
{}
|
|
||||||
@@ -321,7 +321,7 @@
|
|||||||
"post_body.check_for_out_of_channel_groups_mentions.message": "nie zostali powiadomieni poprzez tą wzmiankę, ponieważ nie są na kanale. Nie można ich dodać do kanału, ponieważ nie są członkami grup połączonych. Aby dodać je do tego kanału, muszą zostać dodane do połączonych grup.",
|
"post_body.check_for_out_of_channel_groups_mentions.message": "nie zostali powiadomieni poprzez tą wzmiankę, ponieważ nie są na kanale. Nie można ich dodać do kanału, ponieważ nie są członkami grup połączonych. Aby dodać je do tego kanału, muszą zostać dodane do połączonych grup.",
|
||||||
"post_body.check_for_out_of_channel_mentions.link.and": " i ",
|
"post_body.check_for_out_of_channel_mentions.link.and": " i ",
|
||||||
"post_body.check_for_out_of_channel_mentions.link.private": "dodaj je do tego prywatnego kanału",
|
"post_body.check_for_out_of_channel_mentions.link.private": "dodaj je do tego prywatnego kanału",
|
||||||
"post_body.check_for_out_of_channel_mentions.link.public": "dodać ich do kanału",
|
"post_body.check_for_out_of_channel_mentions.link.public": "dodaj je do kanału",
|
||||||
"post_body.check_for_out_of_channel_mentions.message_last": "? Będą mieć dostęp do całej historii wiadomości.",
|
"post_body.check_for_out_of_channel_mentions.message_last": "? Będą mieć dostęp do całej historii wiadomości.",
|
||||||
"post_body.check_for_out_of_channel_mentions.message.multiple": "zostały wspomniane, ale nie są w kanale. Czy chciałbyś ",
|
"post_body.check_for_out_of_channel_mentions.message.multiple": "zostały wspomniane, ale nie są w kanale. Czy chciałbyś ",
|
||||||
"post_body.check_for_out_of_channel_mentions.message.one": "został wspomniany, ale nie ma go w kanale. Czy chciałbyś ",
|
"post_body.check_for_out_of_channel_mentions.message.one": "został wspomniany, ale nie ma go w kanale. Czy chciałbyś ",
|
||||||
|
|||||||
@@ -433,7 +433,7 @@
|
|||||||
"mobile.create_direct_message.you": "@{username} - вы",
|
"mobile.create_direct_message.you": "@{username} - вы",
|
||||||
"mobile.create_direct_message.start": "Начать разговор",
|
"mobile.create_direct_message.start": "Начать разговор",
|
||||||
"mobile.create_channel.title": "Новый канал",
|
"mobile.create_channel.title": "Новый канал",
|
||||||
"mobile.components.select_server_view.msg_description": "Сервер - это коммуникационный узел вашей команды, доступ к которому осуществляется по уникальному URL-адресу",
|
"mobile.components.select_server_view.msg_description": "Сервер - это коммуникационный узел вашей команды, доступ к которому осуществляется через уникальный URL-адрес",
|
||||||
"mobile.components.select_server_view.msg_connect": "Давайте подключимся к серверу",
|
"mobile.components.select_server_view.msg_connect": "Давайте подключимся к серверу",
|
||||||
"mobile.components.select_server_view.displayName": "Отображаемое имя",
|
"mobile.components.select_server_view.displayName": "Отображаемое имя",
|
||||||
"mobile.components.select_server_view.displayHelp": "Выберите отображаемое имя для вашего сервера",
|
"mobile.components.select_server_view.displayHelp": "Выберите отображаемое имя для вашего сервера",
|
||||||
@@ -992,19 +992,5 @@
|
|||||||
"mobile.calls_recording_start_no_permissions": "У вас нет прав для начала записи. Пожалуйста, попросите ведущего звонка начать запись.",
|
"mobile.calls_recording_start_no_permissions": "У вас нет прав для начала записи. Пожалуйста, попросите ведущего звонка начать запись.",
|
||||||
"mobile.calls_recording_start_in_progress": "Запись уже ведется.",
|
"mobile.calls_recording_start_in_progress": "Запись уже ведется.",
|
||||||
"mobile.calls_host_rec_error_title": "Что-то пошло не так с записью",
|
"mobile.calls_host_rec_error_title": "Что-то пошло не так с записью",
|
||||||
"mobile.calls_host_rec_error": "Пожалуйста, попробуйте записать еще раз. Вы также можете обратиться к системному администратору за помощью в устранении неполадок.",
|
"mobile.calls_host_rec_error": "Пожалуйста, попробуйте записать еще раз. Вы также можете обратиться к системному администратору за помощью в устранении неполадок."
|
||||||
"notification.no_post": "Сообщение не найдено.",
|
|
||||||
"notification.no_connection": "Сервер недоступен, и мы не смогли получить уведомления канала / команды.",
|
|
||||||
"invite.summary.back": "Вернуться назад",
|
|
||||||
"channel_notification_preferences.unmute_content": "Включить уведомления",
|
|
||||||
"channel_notification_preferences.thread_replies": "Ответы на обсуждения",
|
|
||||||
"channel_notification_preferences.reset_default": "Сбросить до значений по умолчанию",
|
|
||||||
"channel_notification_preferences.notify_about": "Уведомить меня о...",
|
|
||||||
"channel_notification_preferences.notification.thread_replies": "Уведомлять меня об ответах на обсуждения, за которыми я слежу в этом канале",
|
|
||||||
"channel_notification_preferences.notification.none": "Ничего",
|
|
||||||
"channel_notification_preferences.notification.mention": "Только упоминания, личные сообщения",
|
|
||||||
"channel_notification_preferences.notification.all": "Все новые сообщения",
|
|
||||||
"channel_notification_preferences.muted_title": "На этом канале уведомления отключены",
|
|
||||||
"channel_notification_preferences.muted_content": "Вы можете изменить настройки уведомлений, но вы не будете получать уведомления, пока вы не включите их для канала обратно.",
|
|
||||||
"channel_notification_preferences.default": "(по умолчанию)"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -992,18 +992,5 @@
|
|||||||
"mobile.calls_recording_start_no_permissions": "Du har inte behörighet att starta en inspelning. Be samtalsvärden att starta en inspelning.",
|
"mobile.calls_recording_start_no_permissions": "Du har inte behörighet att starta en inspelning. Be samtalsvärden att starta en inspelning.",
|
||||||
"mobile.calls_recording_start_in_progress": "En inspelning pågår redan.",
|
"mobile.calls_recording_start_in_progress": "En inspelning pågår redan.",
|
||||||
"mobile.calls_host_rec_error_title": "Något gick fel med inspelningen",
|
"mobile.calls_host_rec_error_title": "Något gick fel med inspelningen",
|
||||||
"mobile.calls_host_rec_error": "Försök att spela in igen. Du kan också kontakta din systemadministratör för att få hjälp med felsökning.",
|
"mobile.calls_host_rec_error": "Försök att spela in igen. Du kan också kontakta din systemadministratör för att få hjälp med felsökning."
|
||||||
"notification.no_post": "Meddelandet hittades inte.",
|
|
||||||
"invite.summary.back": "Gå tillbaka",
|
|
||||||
"channel_notification_preferences.unmute_content": "Stäng av mjutad kanal",
|
|
||||||
"channel_notification_preferences.thread_replies": "Svar i trådar",
|
|
||||||
"channel_notification_preferences.reset_default": "Återställ till standardinställningen",
|
|
||||||
"channel_notification_preferences.notify_about": "Notifiera mig om...",
|
|
||||||
"channel_notification_preferences.notification.thread_replies": "Notifiera mig om svar i trådar som jag följer i denna kanal",
|
|
||||||
"channel_notification_preferences.notification.none": "Inget",
|
|
||||||
"channel_notification_preferences.notification.mention": "Endast omnämnanden och direktmeddelanden",
|
|
||||||
"channel_notification_preferences.notification.all": "Alla nya meddelanden",
|
|
||||||
"channel_notification_preferences.muted_title": "Kanalen är mjutad",
|
|
||||||
"channel_notification_preferences.muted_content": "Du kan ändra notifieringsinställningarna, men eftersom kanalen är mjutad kommer du inte få notifieringar.",
|
|
||||||
"channel_notification_preferences.default": "(standard)"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import {isAndroid, isIos, timeouts, wait} from '@support/utils';
|
import {isIos, timeouts, wait} from '@support/utils';
|
||||||
import {expect} from 'detox';
|
import {expect} from 'detox';
|
||||||
|
|
||||||
class ServerScreen {
|
class ServerScreen {
|
||||||
@@ -47,9 +47,7 @@ class ServerScreen {
|
|||||||
await this.serverUrlInput.replaceText(serverUrl);
|
await this.serverUrlInput.replaceText(serverUrl);
|
||||||
await this.serverUrlInput.tapReturnKey();
|
await this.serverUrlInput.tapReturnKey();
|
||||||
await this.serverDisplayNameInput.replaceText(serverDisplayName);
|
await this.serverDisplayNameInput.replaceText(serverDisplayName);
|
||||||
if (isAndroid()) {
|
await this.serverDisplayNameInput.tapReturnKey();
|
||||||
await this.serverDisplayNameInput.tapReturnKey();
|
|
||||||
}
|
|
||||||
if (isIos()) {
|
if (isIos()) {
|
||||||
await this.tapConnectButton();
|
await this.tapConnectButton();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ describe('Channels - Channel List', () => {
|
|||||||
// * Verify on first channel
|
// * Verify on first channel
|
||||||
await ChannelScreen.toBeVisible();
|
await ChannelScreen.toBeVisible();
|
||||||
await expect(ChannelScreen.headerTitle).toHaveText(testChannel.display_name);
|
await expect(ChannelScreen.headerTitle).toHaveText(testChannel.display_name);
|
||||||
|
await expect(ChannelScreen.introDisplayName).toHaveText(testChannel.display_name);
|
||||||
|
|
||||||
// # Go back to channel list screen and tap on a second channel
|
// # Go back to channel list screen and tap on a second channel
|
||||||
await ChannelScreen.back();
|
await ChannelScreen.back();
|
||||||
@@ -91,15 +92,7 @@ describe('Channels - Channel List', () => {
|
|||||||
// * Verify on second channel
|
// * Verify on second channel
|
||||||
await ChannelScreen.toBeVisible();
|
await ChannelScreen.toBeVisible();
|
||||||
await expect(ChannelScreen.headerTitle).toHaveText('Off-Topic');
|
await expect(ChannelScreen.headerTitle).toHaveText('Off-Topic');
|
||||||
|
await expect(ChannelScreen.introDisplayName).toHaveText('Off-Topic');
|
||||||
// # Go back to channel list screen and tap on a third channel
|
|
||||||
await ChannelScreen.back();
|
|
||||||
await ChannelListScreen.toBeVisible();
|
|
||||||
await ChannelListScreen.getChannelItemDisplayName(channelsCategory, townSquareChannelName).tap();
|
|
||||||
|
|
||||||
// * Verify on third channel
|
|
||||||
await ChannelScreen.toBeVisible();
|
|
||||||
await expect(ChannelScreen.headerTitle).toHaveText('Town Square');
|
|
||||||
|
|
||||||
// # Go back to channel list screen
|
// # Go back to channel list screen
|
||||||
await ChannelScreen.back();
|
await ChannelScreen.back();
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
CFPropertyList (3.0.6)
|
CFPropertyList (3.0.5)
|
||||||
rexml
|
rexml
|
||||||
addressable (2.8.1)
|
addressable (2.8.1)
|
||||||
public_suffix (>= 2.0.2, < 6.0)
|
public_suffix (>= 2.0.2, < 6.0)
|
||||||
artifactory (3.0.15)
|
artifactory (3.0.15)
|
||||||
atomos (0.1.3)
|
atomos (0.1.3)
|
||||||
aws-eventstream (1.2.0)
|
aws-eventstream (1.2.0)
|
||||||
aws-partitions (1.721.0)
|
aws-partitions (1.695.0)
|
||||||
aws-sdk-core (3.170.0)
|
aws-sdk-core (3.169.0)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
aws-partitions (~> 1, >= 1.651.0)
|
aws-partitions (~> 1, >= 1.651.0)
|
||||||
aws-sigv4 (~> 1.5)
|
aws-sigv4 (~> 1.5)
|
||||||
jmespath (~> 1, >= 1.6.1)
|
jmespath (~> 1, >= 1.6.1)
|
||||||
aws-sdk-kms (1.63.0)
|
aws-sdk-kms (1.62.0)
|
||||||
aws-sdk-core (~> 3, >= 3.165.0)
|
aws-sdk-core (~> 3, >= 3.165.0)
|
||||||
aws-sigv4 (~> 1.1)
|
aws-sigv4 (~> 1.1)
|
||||||
aws-sdk-s3 (1.119.1)
|
aws-sdk-s3 (1.118.0)
|
||||||
aws-sdk-core (~> 3, >= 3.165.0)
|
aws-sdk-core (~> 3, >= 3.165.0)
|
||||||
aws-sdk-kms (~> 1)
|
aws-sdk-kms (~> 1)
|
||||||
aws-sigv4 (~> 1.4)
|
aws-sigv4 (~> 1.4)
|
||||||
@@ -36,7 +36,7 @@ GEM
|
|||||||
unf (>= 0.0.5, < 1.0.0)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
dotenv (2.8.1)
|
dotenv (2.8.1)
|
||||||
emoji_regex (3.2.3)
|
emoji_regex (3.2.3)
|
||||||
excon (0.99.0)
|
excon (0.97.1)
|
||||||
faraday (1.10.3)
|
faraday (1.10.3)
|
||||||
faraday-em_http (~> 1.0)
|
faraday-em_http (~> 1.0)
|
||||||
faraday-em_synchrony (~> 1.0)
|
faraday-em_synchrony (~> 1.0)
|
||||||
@@ -66,7 +66,7 @@ GEM
|
|||||||
faraday_middleware (1.2.0)
|
faraday_middleware (1.2.0)
|
||||||
faraday (~> 1.0)
|
faraday (~> 1.0)
|
||||||
fastimage (2.2.6)
|
fastimage (2.2.6)
|
||||||
fastlane (2.212.1)
|
fastlane (2.211.0)
|
||||||
CFPropertyList (>= 2.3, < 4.0.0)
|
CFPropertyList (>= 2.3, < 4.0.0)
|
||||||
addressable (>= 2.8, < 3.0.0)
|
addressable (>= 2.8, < 3.0.0)
|
||||||
artifactory (~> 3.0)
|
artifactory (~> 3.0)
|
||||||
@@ -109,11 +109,11 @@ GEM
|
|||||||
fastlane-plugin-android_change_string_app_name (0.1.1)
|
fastlane-plugin-android_change_string_app_name (0.1.1)
|
||||||
nokogiri
|
nokogiri
|
||||||
fastlane-plugin-find_replace_string (0.1.0)
|
fastlane-plugin-find_replace_string (0.1.0)
|
||||||
fastlane-plugin-versioning_android (0.1.1)
|
fastlane-plugin-versioning_android (0.1.0)
|
||||||
gh_inspector (1.1.3)
|
gh_inspector (1.1.3)
|
||||||
google-apis-androidpublisher_v3 (0.35.0)
|
google-apis-androidpublisher_v3 (0.32.0)
|
||||||
google-apis-core (>= 0.11.0, < 2.a)
|
google-apis-core (>= 0.9.1, < 2.a)
|
||||||
google-apis-core (0.11.0)
|
google-apis-core (0.9.5)
|
||||||
addressable (~> 2.5, >= 2.5.1)
|
addressable (~> 2.5, >= 2.5.1)
|
||||||
googleauth (>= 0.16.2, < 2.a)
|
googleauth (>= 0.16.2, < 2.a)
|
||||||
httpclient (>= 2.8.1, < 3.a)
|
httpclient (>= 2.8.1, < 3.a)
|
||||||
@@ -122,10 +122,10 @@ GEM
|
|||||||
retriable (>= 2.0, < 4.a)
|
retriable (>= 2.0, < 4.a)
|
||||||
rexml
|
rexml
|
||||||
webrick
|
webrick
|
||||||
google-apis-iamcredentials_v1 (0.17.0)
|
google-apis-iamcredentials_v1 (0.16.0)
|
||||||
google-apis-core (>= 0.11.0, < 2.a)
|
google-apis-core (>= 0.9.1, < 2.a)
|
||||||
google-apis-playcustomapp_v1 (0.13.0)
|
google-apis-playcustomapp_v1 (0.12.0)
|
||||||
google-apis-core (>= 0.11.0, < 2.a)
|
google-apis-core (>= 0.9.1, < 2.a)
|
||||||
google-apis-storage_v1 (0.19.0)
|
google-apis-storage_v1 (0.19.0)
|
||||||
google-apis-core (>= 0.9.0, < 2.a)
|
google-apis-core (>= 0.9.0, < 2.a)
|
||||||
google-cloud-core (1.6.0)
|
google-cloud-core (1.6.0)
|
||||||
@@ -133,7 +133,7 @@ GEM
|
|||||||
google-cloud-errors (~> 1.0)
|
google-cloud-errors (~> 1.0)
|
||||||
google-cloud-env (1.6.0)
|
google-cloud-env (1.6.0)
|
||||||
faraday (>= 0.17.3, < 3.0)
|
faraday (>= 0.17.3, < 3.0)
|
||||||
google-cloud-errors (1.3.1)
|
google-cloud-errors (1.3.0)
|
||||||
google-cloud-storage (1.44.0)
|
google-cloud-storage (1.44.0)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
digest-crc (~> 0.4)
|
digest-crc (~> 0.4)
|
||||||
@@ -155,7 +155,7 @@ GEM
|
|||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
jmespath (1.6.2)
|
jmespath (1.6.2)
|
||||||
json (2.6.3)
|
json (2.6.3)
|
||||||
jwt (2.7.0)
|
jwt (2.6.0)
|
||||||
memoist (0.16.2)
|
memoist (0.16.2)
|
||||||
mini_magick (4.12.0)
|
mini_magick (4.12.0)
|
||||||
mini_mime (1.1.2)
|
mini_mime (1.1.2)
|
||||||
@@ -164,12 +164,12 @@ GEM
|
|||||||
multipart-post (2.0.0)
|
multipart-post (2.0.0)
|
||||||
nanaimo (0.3.0)
|
nanaimo (0.3.0)
|
||||||
naturally (2.2.1)
|
naturally (2.2.1)
|
||||||
nokogiri (1.14.2)
|
nokogiri (1.14.0)
|
||||||
mini_portile2 (~> 2.8.0)
|
mini_portile2 (~> 2.8.0)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
optparse (0.1.1)
|
optparse (0.1.1)
|
||||||
os (1.1.4)
|
os (1.1.4)
|
||||||
plist (3.7.0)
|
plist (3.6.0)
|
||||||
public_suffix (5.0.1)
|
public_suffix (5.0.1)
|
||||||
racc (1.6.2)
|
racc (1.6.2)
|
||||||
rake (13.0.6)
|
rake (13.0.6)
|
||||||
@@ -188,7 +188,7 @@ GEM
|
|||||||
faraday (>= 0.17.5, < 3.a)
|
faraday (>= 0.17.5, < 3.a)
|
||||||
jwt (>= 1.5, < 3.0)
|
jwt (>= 1.5, < 3.0)
|
||||||
multi_json (~> 1.10)
|
multi_json (~> 1.10)
|
||||||
simctl (1.6.10)
|
simctl (1.6.8)
|
||||||
CFPropertyList
|
CFPropertyList
|
||||||
naturally
|
naturally
|
||||||
slack-notifier (2.3.2)
|
slack-notifier (2.3.2)
|
||||||
@@ -205,7 +205,7 @@ GEM
|
|||||||
unf_ext
|
unf_ext
|
||||||
unf_ext (0.0.8.2)
|
unf_ext (0.0.8.2)
|
||||||
unicode-display_width (1.8.0)
|
unicode-display_width (1.8.0)
|
||||||
webrick (1.8.1)
|
webrick (1.7.0)
|
||||||
word_wrap (1.0.0)
|
word_wrap (1.0.0)
|
||||||
xcodeproj (1.22.0)
|
xcodeproj (1.22.0)
|
||||||
CFPropertyList (>= 2.3.3, < 4.0)
|
CFPropertyList (>= 2.3.3, < 4.0)
|
||||||
|
|||||||
@@ -1128,7 +1128,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
CURRENT_PROJECT_VERSION = 461;
|
CURRENT_PROJECT_VERSION = 460;
|
||||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
@@ -1172,7 +1172,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
CURRENT_PROJECT_VERSION = 461;
|
CURRENT_PROJECT_VERSION = 460;
|
||||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
@@ -1315,7 +1315,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 461;
|
CURRENT_PROJECT_VERSION = 460;
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
@@ -1366,7 +1366,7 @@
|
|||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
CURRENT_PROJECT_VERSION = 461;
|
CURRENT_PROJECT_VERSION = 460;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>461</string>
|
<string>460</string>
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>2.1.0</string>
|
<string>2.1.0</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>461</string>
|
<string>460</string>
|
||||||
<key>UIAppFonts</key>
|
<key>UIAppFonts</key>
|
||||||
<array>
|
<array>
|
||||||
<string>OpenSans-Bold.ttf</string>
|
<string>OpenSans-Bold.ttf</string>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>2.1.0</string>
|
<string>2.1.0</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>461</string>
|
<string>460</string>
|
||||||
<key>NSExtension</key>
|
<key>NSExtension</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSExtensionPointIdentifier</key>
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
|||||||
@@ -19,6 +19,6 @@ module.exports = {
|
|||||||
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/test/file_transformer.js',
|
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/test/file_transformer.js',
|
||||||
},
|
},
|
||||||
transformIgnorePatterns: [
|
transformIgnorePatterns: [
|
||||||
'node_modules/(?!(@react-native|react-native)|jail-monkey|@sentry/react-native|react-clone-referenced-element|@react-native-community|react-navigation|@react-navigation/.*|validator|react-syntax-highlighter/.*|hast-util-from-selector|hastscript|property-information|hast-util-parse-selector|space-separated-tokens|comma-separated-tokens|zwitch)',
|
'node_modules/(?!(@react-native|react-native)|jail-monkey|@sentry/react-native|react-clone-referenced-element|@react-native-community|react-navigation|@react-navigation/.*|validator|react-syntax-highlighter/.*|hast-util-from-selector|hastscript|property-information|hast-util-parse-selector|space-separated-tokens|comma-separated-tokens|zwitch|@mmcalls/common)',
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
39
package-lock.json
generated
39
package-lock.json
generated
@@ -19,10 +19,11 @@
|
|||||||
"@gorhom/bottom-sheet": "4.4.5",
|
"@gorhom/bottom-sheet": "4.4.5",
|
||||||
"@mattermost/compass-icons": "0.1.35",
|
"@mattermost/compass-icons": "0.1.35",
|
||||||
"@mattermost/react-native-emm": "1.3.5",
|
"@mattermost/react-native-emm": "1.3.5",
|
||||||
"@mattermost/react-native-network-client": "1.3.2",
|
"@mattermost/react-native-network-client": "1.3.1",
|
||||||
"@mattermost/react-native-paste-input": "0.6.2",
|
"@mattermost/react-native-paste-input": "0.6.2",
|
||||||
"@mattermost/react-native-turbo-log": "0.2.3",
|
"@mattermost/react-native-turbo-log": "0.2.3",
|
||||||
"@mattermost/react-native-turbo-mailer": "0.2.4",
|
"@mattermost/react-native-turbo-mailer": "0.2.4",
|
||||||
|
"@mmcalls/common": "https://gitpkg.now.sh/mattermost/mattermost-plugin-calls/webapp/packages/common?b5e89f3",
|
||||||
"@msgpack/msgpack": "2.8.0",
|
"@msgpack/msgpack": "2.8.0",
|
||||||
"@nozbe/watermelondb": "0.25.5",
|
"@nozbe/watermelondb": "0.25.5",
|
||||||
"@nozbe/with-observables": "1.4.1",
|
"@nozbe/with-observables": "1.4.1",
|
||||||
@@ -124,6 +125,7 @@
|
|||||||
"@types/jest": "29.4.0",
|
"@types/jest": "29.4.0",
|
||||||
"@types/lodash": "4.14.191",
|
"@types/lodash": "4.14.191",
|
||||||
"@types/mime-db": "1.43.1",
|
"@types/mime-db": "1.43.1",
|
||||||
|
"@types/pako": "2.0.0",
|
||||||
"@types/querystringify": "2.0.0",
|
"@types/querystringify": "2.0.0",
|
||||||
"@types/react": "18.0.28",
|
"@types/react": "18.0.28",
|
||||||
"@types/react-native-background-timer": "2.0.0",
|
"@types/react-native-background-timer": "2.0.0",
|
||||||
@@ -3241,9 +3243,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mattermost/react-native-network-client": {
|
"node_modules/@mattermost/react-native-network-client": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@mattermost/react-native-network-client/-/react-native-network-client-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@mattermost/react-native-network-client/-/react-native-network-client-1.3.1.tgz",
|
||||||
"integrity": "sha512-3GFNzMXZWlIXXDYQLIJlKRf+HUZKP0F7mpZ1rSTgoTmUeFdqde4uRiU/L96COg34rAdeFRFrgpk0DxEnT7NiVg==",
|
"integrity": "sha512-DtwVLV/NUE6MkXOlVZG+4QJXou6nHMdmsxnP1+RqhOeSw5jJlQvxmQgxzxvxLpaWOag+wgB1zpDulGNbr/Cz6Q==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"validator": "13.9.0",
|
"validator": "13.9.0",
|
||||||
"zod": "3.20.6"
|
"zod": "3.20.6"
|
||||||
@@ -3286,6 +3288,13 @@
|
|||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@mmcalls/common": {
|
||||||
|
"name": "@calls/common",
|
||||||
|
"version": "0.12.0",
|
||||||
|
"resolved": "https://gitpkg.now.sh/mattermost/mattermost-plugin-calls/webapp/packages/common?b5e89f3",
|
||||||
|
"integrity": "sha512-lz+T2HHQBfQDpgrI4CkrSNkmBjIaKxJGSxSZHSL0LQ8xAlRUBo3WMG5wJlwwzpxP6rixJjUnoyKBSo8Jn0wWQQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@msgpack/msgpack": {
|
"node_modules/@msgpack/msgpack": {
|
||||||
"version": "2.8.0",
|
"version": "2.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.8.0.tgz",
|
||||||
@@ -5957,6 +5966,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz",
|
||||||
"integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA=="
|
"integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/pako": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-10+iaz93qR5WYxTo+PMifD5TSxiOtdRaxBf7INGGXMQgTCu8Z/7GYWYFUOS3q/G0nE5boj1r4FEB+WSy7s5gbA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/parse-json": {
|
"node_modules/@types/parse-json": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
||||||
@@ -24297,9 +24312,9 @@
|
|||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"@mattermost/react-native-network-client": {
|
"@mattermost/react-native-network-client": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@mattermost/react-native-network-client/-/react-native-network-client-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@mattermost/react-native-network-client/-/react-native-network-client-1.3.1.tgz",
|
||||||
"integrity": "sha512-3GFNzMXZWlIXXDYQLIJlKRf+HUZKP0F7mpZ1rSTgoTmUeFdqde4uRiU/L96COg34rAdeFRFrgpk0DxEnT7NiVg==",
|
"integrity": "sha512-DtwVLV/NUE6MkXOlVZG+4QJXou6nHMdmsxnP1+RqhOeSw5jJlQvxmQgxzxvxLpaWOag+wgB1zpDulGNbr/Cz6Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"validator": "13.9.0",
|
"validator": "13.9.0",
|
||||||
"zod": "3.20.6"
|
"zod": "3.20.6"
|
||||||
@@ -24325,6 +24340,10 @@
|
|||||||
"integrity": "sha512-6W37UvLxg7vhP5YJXZfzKvPy4r9bozqSSuB4gbC2EjvWrGB4LfwKWljgw+Gb/E8x3ceMCib2SPPMz+thzs7DHw==",
|
"integrity": "sha512-6W37UvLxg7vhP5YJXZfzKvPy4r9bozqSSuB4gbC2EjvWrGB4LfwKWljgw+Gb/E8x3ceMCib2SPPMz+thzs7DHw==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
|
"@mmcalls/common": {
|
||||||
|
"version": "https://gitpkg.now.sh/mattermost/mattermost-plugin-calls/webapp/packages/common?b5e89f3",
|
||||||
|
"integrity": "sha512-lz+T2HHQBfQDpgrI4CkrSNkmBjIaKxJGSxSZHSL0LQ8xAlRUBo3WMG5wJlwwzpxP6rixJjUnoyKBSo8Jn0wWQQ=="
|
||||||
|
},
|
||||||
"@msgpack/msgpack": {
|
"@msgpack/msgpack": {
|
||||||
"version": "2.8.0",
|
"version": "2.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.8.0.tgz",
|
||||||
@@ -26328,6 +26347,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz",
|
||||||
"integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA=="
|
"integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA=="
|
||||||
},
|
},
|
||||||
|
"@types/pako": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-10+iaz93qR5WYxTo+PMifD5TSxiOtdRaxBf7INGGXMQgTCu8Z/7GYWYFUOS3q/G0nE5boj1r4FEB+WSy7s5gbA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/parse-json": {
|
"@types/parse-json": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
||||||
|
|||||||
@@ -16,10 +16,11 @@
|
|||||||
"@gorhom/bottom-sheet": "4.4.5",
|
"@gorhom/bottom-sheet": "4.4.5",
|
||||||
"@mattermost/compass-icons": "0.1.35",
|
"@mattermost/compass-icons": "0.1.35",
|
||||||
"@mattermost/react-native-emm": "1.3.5",
|
"@mattermost/react-native-emm": "1.3.5",
|
||||||
"@mattermost/react-native-network-client": "1.3.2",
|
"@mattermost/react-native-network-client": "1.3.1",
|
||||||
"@mattermost/react-native-paste-input": "0.6.2",
|
"@mattermost/react-native-paste-input": "0.6.2",
|
||||||
"@mattermost/react-native-turbo-log": "0.2.3",
|
"@mattermost/react-native-turbo-log": "0.2.3",
|
||||||
"@mattermost/react-native-turbo-mailer": "0.2.4",
|
"@mattermost/react-native-turbo-mailer": "0.2.4",
|
||||||
|
"@mmcalls/common": "https://gitpkg.now.sh/mattermost/mattermost-plugin-calls/webapp/packages/common?b5e89f3",
|
||||||
"@msgpack/msgpack": "2.8.0",
|
"@msgpack/msgpack": "2.8.0",
|
||||||
"@nozbe/watermelondb": "0.25.5",
|
"@nozbe/watermelondb": "0.25.5",
|
||||||
"@nozbe/with-observables": "1.4.1",
|
"@nozbe/with-observables": "1.4.1",
|
||||||
@@ -121,6 +122,7 @@
|
|||||||
"@types/jest": "29.4.0",
|
"@types/jest": "29.4.0",
|
||||||
"@types/lodash": "4.14.191",
|
"@types/lodash": "4.14.191",
|
||||||
"@types/mime-db": "1.43.1",
|
"@types/mime-db": "1.43.1",
|
||||||
|
"@types/pako": "2.0.0",
|
||||||
"@types/querystringify": "2.0.0",
|
"@types/querystringify": "2.0.0",
|
||||||
"@types/react": "18.0.28",
|
"@types/react": "18.0.28",
|
||||||
"@types/react-native-background-timer": "2.0.0",
|
"@types/react-native-background-timer": "2.0.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user