mattermost-mobile-upstream/app/screens/channel_settings/index.ts
Elias Nahum b6fc85385e
Migrate from RNN to Expo router (#9402)
* Expo-Router partial migration

* Full migration to ExpoRouter

* i18n

* add tests

* Merge branch 'main' into expo-router

* align package-lock.json

* i18n

* feedback review

* feedback review

* android 16kb patch

* Refactored emoji picker keyboard using a state machine

* set android launch_screen in drawables instead of layout

* no conditional hooks

* remove duplicate call in test

* address feedback

* fix tests and address more feedback

* small corrections

* small corrections

* small corrections

* fix draft test

* fix draft test 2

* remove navigateToScreen in draff test

* fix nit in draft test

* remove doc file

* Feedback review

* fix snapshots

* improve test coverage

* fix i18n

* Upgrade t o RN 0.83, other dependencies and new architecture (#9737)

* library hardware-keyboard android new arch support fix

* library rnshare android new arch support fix

* library rnutils android new arch support fix

* library secure-pdf-viewer add new arch support

* Enable new arch on iOS and Android

* update emm and network client with new-arch

* update intune to use new-arch

* performance improvements

* fix tests

* fixes post input on new-arch

* fix android emoji picker

* remove doc

* run tests in parallel

* upgrade to React Native 0.83.9, Expo 55, React 19, and New Architecture

Bumps core dependencies to their latest versions:
- React Native 0.77.3 → 0.83.9 (New Architecture enabled)
- React 18.3.1 → 19.2.5
- Expo SDK 52 → 55
- TypeScript 5.8.3 → 6.0.3
- Node.js 22.16.0 → 24.15.0
- react-native-reanimated 3.17.3 → 4.3.0
- Various other dependency upgrades

Migrates iOS AppDelegate from Objective-C to Swift, updates Android
build tooling and Gradle wrapper, refreshes all patches and font
assets, and adapts app code, tests, and CI workflows throughout for
API changes introduced by these upgrades.

* remove unecessary dismissKeyboard in notifications

* feedback review

* update intune to use new-arch

* update @mattermost deps

* missing i18n string

* additional fixes

* update fastlane
2026-05-13 17:44:55 +08:00

208 lines
8.1 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {withDatabase, withObservables} from '@nozbe/watermelondb/react';
import {combineLatest, of as of$} from 'rxjs';
import {distinctUntilChanged, map, switchMap, combineLatestWith} from 'rxjs/operators';
import {observeIsCallsEnabledInChannel} from '@calls/observers';
import {observeCallsConfig} from '@calls/state';
import {General, Permissions} from '@constants';
import {observeChannel} from '@queries/servers/channel';
import {observePermissionForChannel, observePermissionForTeam, observeCanManageChannelSettings, observeCanManageChannelAutotranslations, observeCanManageSharedChannel} from '@queries/servers/role';
import {
observeConfigValue,
observeConfigBooleanValue,
observeCurrentTeamId,
} from '@queries/servers/system';
import {observeCurrentTeam} from '@queries/servers/team';
import {observeCurrentUser, observeUserIsChannelAdmin, observeUserIsTeamAdmin} from '@queries/servers/user';
import {isTypeDMorGM, isDefaultChannel} from '@utils/channel';
import {isMinimumServerVersion} from '@utils/helpers';
import {isSystemAdmin} from '@utils/user';
import ChannelSettings from './channel_settings';
import type {WithDatabaseArgs} from '@typings/database/database';
type Props = WithDatabaseArgs & {
channelId: string;
serverUrl: string;
}
const enhanced = withObservables(['channelId'], ({channelId, serverUrl, database}: Props) => {
const channel = observeChannel(database, channelId);
const type = channel.pipe(switchMap((c) => of$(c?.type)));
const teamId = channel.pipe(switchMap((c) => (c?.teamId ? of$(c.teamId) : observeCurrentTeamId(database))));
const currentUser = observeCurrentUser(database);
const team = observeCurrentTeam(database);
const isTeamAdmin = combineLatest([teamId, currentUser]).pipe(
switchMap(([tId, u]) => (u ? observeUserIsTeamAdmin(database, u.id, tId) : of$(false))),
);
// Calls observables
const callsPluginEnabled = observeCallsConfig(serverUrl).pipe(
switchMap((config) => of$(config.pluginEnabled)),
distinctUntilChanged(),
);
const callsDefaultEnabled = observeCallsConfig(serverUrl).pipe(
switchMap((config) => of$(config.DefaultEnabled)),
distinctUntilChanged(),
);
const allowEnableCalls = observeCallsConfig(serverUrl).pipe(
switchMap((config) => of$(config.AllowEnableCalls)),
distinctUntilChanged(),
);
const systemAdmin = currentUser.pipe(
switchMap((u) => (u ? of$(u.roles) : of$(''))),
switchMap((roles) => of$(isSystemAdmin(roles || ''))),
distinctUntilChanged(),
);
const channelAdmin = currentUser.pipe(
switchMap((u) => (u ? observeUserIsChannelAdmin(database, u.id, channelId) : of$(false))),
distinctUntilChanged(),
);
const serverVersion = observeConfigValue(database, 'Version');
const callsGAServer = serverVersion.pipe(
switchMap((v) => of$(isMinimumServerVersion(v || '', 7, 6))),
);
const dmOrGM = type.pipe(switchMap((t) => of$(isTypeDMorGM(t))));
const canEnableDisableCalls = combineLatest([callsPluginEnabled, callsDefaultEnabled, allowEnableCalls, systemAdmin, channelAdmin, callsGAServer, dmOrGM, isTeamAdmin]).pipe(
switchMap(([pluginEnabled, liveMode, allow, sysAdmin, chAdmin, gaServer, dmGM, tAdmin]) => {
if (!pluginEnabled) {
return of$(false);
}
if (gaServer) {
if (allow && !liveMode) {
return of$(Boolean(sysAdmin));
}
if (allow && liveMode) {
return of$(Boolean(chAdmin || tAdmin || sysAdmin || dmGM));
}
return of$(false);
}
// now we're pre GA 7.6
if (allow && liveMode) {
return of$(Boolean(chAdmin || sysAdmin || dmGM));
}
if (allow && !liveMode) {
return of$(Boolean(sysAdmin || chAdmin || dmGM));
}
if (!allow) {
return of$(Boolean(sysAdmin));
}
return of$(false);
}),
);
const isCallsEnabledInChannel = observeIsCallsEnabledInChannel(database, serverUrl, of$(channelId));
const canManageSettings = currentUser.pipe(
switchMap((u) => (u ? observeCanManageChannelSettings(database, channelId, u) : of$(false))),
distinctUntilChanged(),
);
// canConvert observable (for Convert to private)
const canConvert = channel.pipe(
combineLatestWith(currentUser),
switchMap(([ch, u]) => {
if (!ch || !u || isDefaultChannel(ch)) {
return of$(false);
}
if (ch.type !== General.OPEN_CHANNEL) {
return of$(false);
}
return observePermissionForChannel(database, ch, u, Permissions.CONVERT_PUBLIC_CHANNEL_TO_PRIVATE, false);
}),
);
// Archive observables
const isArchived = channel.pipe(switchMap((c) => of$((c?.deleteAt || 0) > 0)));
const canLeave = channel.pipe(
combineLatestWith(currentUser),
switchMap(([ch, u]) => {
const isDC = isDefaultChannel(ch);
return of$(!isDC || (isDC && u?.isGuest));
}),
);
const canArchive = channel.pipe(
combineLatestWith(currentUser, canLeave, isArchived, type),
switchMap(([ch, u, leave, archived, chType]) => {
if (
chType === General.DM_CHANNEL || chType === General.GM_CHANNEL ||
!ch || !u || !leave || archived
) {
return of$(false);
}
if (chType === General.OPEN_CHANNEL) {
return observePermissionForChannel(database, ch, u, Permissions.DELETE_PUBLIC_CHANNEL, true);
}
return observePermissionForChannel(database, ch, u, Permissions.DELETE_PRIVATE_CHANNEL, true);
}),
);
const canUnarchive = team.pipe(
combineLatestWith(currentUser, isArchived, type),
switchMap(([t, u, archived, chType]) => {
if (
chType === General.DM_CHANNEL || chType === General.GM_CHANNEL ||
!t || !u || !archived
) {
return of$(false);
}
return observePermissionForTeam(database, t, u, Permissions.MANAGE_TEAM, false);
}),
);
// Convert GM to channel observable
const isGuestUser = currentUser.pipe(
switchMap((u) => (u ? of$(u.isGuest) : of$(false))),
distinctUntilChanged(),
);
const isConvertGMFeatureAvailable = serverVersion.pipe(
switchMap((version) => of$(isMinimumServerVersion(version || '', 9, 1))),
);
const convertGMOptionAvailable = combineLatest([isConvertGMFeatureAvailable, type, isGuestUser]).pipe(
switchMap(([available, chType, guest]) => of$(available && chType === General.GM_CHANNEL && !guest)),
);
// Channel autotranslation observable
const canManageAutotranslations = currentUser.pipe(
switchMap((u) => (u ? observeCanManageChannelAutotranslations(database, channelId, u) : of$(false))),
);
// Shared channels (connected workspaces)
const sharedChannelsEnabled = observeConfigBooleanValue(database, 'ExperimentalSharedChannels');
const canManageSharedChannelPermission = currentUser.pipe(
switchMap((u) => (u ? observeCanManageSharedChannel(database, channelId, u) : of$(false))),
);
const canManageSharedChannel = combineLatest([canManageSharedChannelPermission, sharedChannelsEnabled]).pipe(
map(([canManage, enabled]) => canManage && enabled),
);
return {
canArchive,
canConvert,
canEnableDisableCalls,
canManageSettings,
canUnarchive,
convertGMOptionAvailable,
displayName: channel.pipe(switchMap((c) => of$(c?.displayName || ''))),
isCallsEnabledInChannel,
canManageAutotranslations,
canManageSharedChannel,
isGuestUser,
type,
};
});
export default withDatabase(enhanced(ChannelSettings));