forked from Ivasoft/mattermost-mobile
[Gekidou] various fixes (#5951)
* Specify the screen to be dismissed from the bottom sheet on tablets * reposition unread badge on server icon * Skip setNavigatorStyles and set correct theme for login flow screens * Fetch current user status with fetchMe and when the WS connects * switchToChannel skip setting current channel if id is the same * ChannelMention component to use switchToChannelById instead of switchToChannel * loginEntry only set initialChannel if isTablet * deferredAppEntryActions mark channel as read and fetch channel stats if initialChannel is set * feedback review * feedback review
This commit is contained in:
@@ -23,11 +23,12 @@ import type UserModel from '@typings/database/models/servers/user';
|
||||
const {SERVER: {CHANNEL_MEMBERSHIP, USER}} = MM_TABLES;
|
||||
|
||||
export const switchToChannel = async (serverUrl: string, channelId: string, teamId?: string, prepareRecordsOnly = false) => {
|
||||
const database = DatabaseManager.serverDatabases[serverUrl]?.database;
|
||||
if (!database) {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
return {error: `${serverUrl} database not found`};
|
||||
}
|
||||
|
||||
const {database} = operator;
|
||||
const models: Model[] = [];
|
||||
try {
|
||||
const dt = Date.now();
|
||||
@@ -37,9 +38,11 @@ export const switchToChannel = async (serverUrl: string, channelId: string, team
|
||||
|
||||
if (member) {
|
||||
const channel: ChannelModel = await member.channel.fetch();
|
||||
const {operator} = DatabaseManager.serverDatabases[serverUrl];
|
||||
const commonValues: PrepareCommonSystemValuesArgs = {currentChannelId: channelId};
|
||||
if (isTabletDevice) {
|
||||
const commonValues: PrepareCommonSystemValuesArgs = {};
|
||||
if (system.currentChannelId !== channelId) {
|
||||
commonValues.currentChannelId = channelId;
|
||||
}
|
||||
if (isTabletDevice && system.currentChannelId !== channelId) {
|
||||
// On tablet, the channel is being rendered, by setting the channel to empty first we speed up
|
||||
// the switch by ~3x
|
||||
await setCurrentChannelId(operator, '');
|
||||
@@ -51,9 +54,11 @@ export const switchToChannel = async (serverUrl: string, channelId: string, team
|
||||
models.push(...history);
|
||||
}
|
||||
|
||||
const common = await prepareCommonSystemValues(operator, commonValues);
|
||||
if (common) {
|
||||
models.push(...common);
|
||||
if (Object.keys(commonValues).length) {
|
||||
const common = await prepareCommonSystemValues(operator, commonValues);
|
||||
if (common) {
|
||||
models.push(...common);
|
||||
}
|
||||
}
|
||||
|
||||
if (system.currentChannelId !== channelId) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {fetchMissingSidebarInfo, fetchMyChannelsForTeam, MyChannelsRequest} from '@actions/remote/channel';
|
||||
import {fetchChannelStats, fetchMissingSidebarInfo, fetchMyChannelsForTeam, markChannelAsRead, MyChannelsRequest} from '@actions/remote/channel';
|
||||
import {fetchGroupsForTeam} from '@actions/remote/group';
|
||||
import {fetchPostsForChannel, fetchPostsForUnreadChannels} from '@actions/remote/post';
|
||||
import {MyPreferencesRequest, fetchMyPreferences} from '@actions/remote/preference';
|
||||
@@ -164,6 +164,8 @@ export const deferredAppEntryActions = async (
|
||||
// defer fetching posts for initial channel
|
||||
if (initialChannelId) {
|
||||
fetchPostsForChannel(serverUrl, initialChannelId);
|
||||
markChannelAsRead(serverUrl, initialChannelId);
|
||||
fetchChannelStats(serverUrl, initialChannelId);
|
||||
}
|
||||
|
||||
// defer sidebar DM & GM profiles
|
||||
|
||||
@@ -5,7 +5,7 @@ import {Model} from '@nozbe/watermelondb';
|
||||
|
||||
import {fetchMyChannelsForTeam, MyChannelsRequest} from '@actions/remote/channel';
|
||||
import {MyPreferencesRequest, fetchMyPreferences} from '@actions/remote/preference';
|
||||
import {fetchRolesIfNeeded} from '@actions/remote/role';
|
||||
import {fetchRolesIfNeeded, RolesRequest} from '@actions/remote/role';
|
||||
import {getSessions} from '@actions/remote/session';
|
||||
import {ConfigAndLicenseRequest, fetchConfigAndLicense} from '@actions/remote/systems';
|
||||
import {fetchMyTeams, MyTeamsRequest} from '@actions/remote/team';
|
||||
@@ -16,8 +16,9 @@ import {selectDefaultTeam} from '@helpers/api/team';
|
||||
import NetworkManager from '@init/network_manager';
|
||||
import {prepareModels} from '@queries/servers/entry';
|
||||
import {prepareCommonSystemValues} from '@queries/servers/system';
|
||||
import {addChannelToTeamHistory} from '@queries/servers/team';
|
||||
import {addChannelToTeamHistory, addTeamToTeamHistory} from '@queries/servers/team';
|
||||
import {selectDefaultChannelForTeam} from '@utils/channel';
|
||||
import {isTablet} from '@utils/helpers';
|
||||
import {scheduleExpiredNotification} from '@utils/notification';
|
||||
|
||||
import {deferredAppEntryActions} from './common';
|
||||
@@ -53,6 +54,7 @@ export const loginEntry = async ({serverUrl, user, deviceToken}: AfterLoginArgs)
|
||||
}
|
||||
|
||||
try {
|
||||
const isTabletDevice = await isTablet();
|
||||
let initialTeam: Team|undefined;
|
||||
let initialChannel: Channel|undefined;
|
||||
let myTeams: Team[]|undefined;
|
||||
@@ -66,6 +68,7 @@ export const loginEntry = async ({serverUrl, user, deviceToken}: AfterLoginArgs)
|
||||
|
||||
const [clData, prefData, teamData] = await Promise.all(promises);
|
||||
let chData: MyChannelsRequest|undefined;
|
||||
let rData: RolesRequest|undefined;
|
||||
|
||||
// schedule local push notification if needed
|
||||
if (clData.config) {
|
||||
@@ -119,10 +122,12 @@ export const loginEntry = async ({serverUrl, user, deviceToken}: AfterLoginArgs)
|
||||
}
|
||||
|
||||
// fetch user roles
|
||||
const rData = await fetchRolesIfNeeded(serverUrl, Array.from(rolesToFetch));
|
||||
rData = await fetchRolesIfNeeded(serverUrl, Array.from(rolesToFetch), true);
|
||||
|
||||
// select initial channel
|
||||
initialChannel = selectDefaultChannelForTeam(channels!, memberships!, initialTeam!.id, rData.roles, user.locale);
|
||||
// select initial channel only on Tablets
|
||||
if (isTabletDevice) {
|
||||
initialChannel = selectDefaultChannelForTeam(channels!, memberships!, initialTeam!.id, rData.roles, user.locale);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,6 +147,11 @@ export const loginEntry = async ({serverUrl, user, deviceToken}: AfterLoginArgs)
|
||||
modelPromises.push(systemModels);
|
||||
}
|
||||
|
||||
if (initialTeam) {
|
||||
const th = addTeamToTeamHistory(operator, initialTeam.id, true);
|
||||
modelPromises.push(th);
|
||||
}
|
||||
|
||||
if (initialTeam && initialChannel) {
|
||||
try {
|
||||
const tch = addChannelToTeamHistory(operator, initialTeam.id, initialChannel.id, true);
|
||||
@@ -151,6 +161,11 @@ export const loginEntry = async ({serverUrl, user, deviceToken}: AfterLoginArgs)
|
||||
}
|
||||
}
|
||||
|
||||
if (rData?.roles?.length) {
|
||||
const roles = operator.handleRole({roles: rData.roles, prepareRecordsOnly: true});
|
||||
modelPromises.push(roles);
|
||||
}
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models.flat() as Model[]);
|
||||
|
||||
@@ -48,7 +48,12 @@ export const fetchMe = async (serverUrl: string, fetchOnly = false): Promise<MyU
|
||||
}
|
||||
|
||||
try {
|
||||
const user = await client.getMe();
|
||||
const [user, userStatus] = await Promise.all<[Promise<UserProfile>, Promise<UserStatus>]>([
|
||||
client.getMe(),
|
||||
client.getStatus('me'),
|
||||
]);
|
||||
|
||||
user.status = userStatus.status;
|
||||
|
||||
if (!fetchOnly) {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
|
||||
@@ -8,7 +8,7 @@ import {fetchPostsForUnreadChannels, fetchPostsSince} from '@actions/remote/post
|
||||
import {fetchRoles} from '@actions/remote/role';
|
||||
import {fetchConfigAndLicense} from '@actions/remote/systems';
|
||||
import {fetchAllTeams, fetchTeamsChannelsAndUnreadPosts} from '@actions/remote/team';
|
||||
import {updateAllUsersSince} from '@actions/remote/user';
|
||||
import {fetchStatusByIds, updateAllUsersSince} from '@actions/remote/user';
|
||||
import {General, WebsocketEvents} from '@constants';
|
||||
import {SYSTEM_IDENTIFIERS} from '@constants/database';
|
||||
import DatabaseManager from '@database/manager';
|
||||
@@ -36,8 +36,9 @@ export async function handleFirstConnect(serverUrl: string) {
|
||||
if (!operator) {
|
||||
return;
|
||||
}
|
||||
const config = await queryConfig(operator.database);
|
||||
const lastDisconnect = await queryWebSocketLastDisconnected(operator.database);
|
||||
const {database} = operator;
|
||||
const config = await queryConfig(database);
|
||||
const lastDisconnect = await queryWebSocketLastDisconnected(database);
|
||||
|
||||
// ESR: 5.37
|
||||
if (lastDisconnect && config.EnableReliableWebSockets !== 'true' && alreadyConnected.has(serverUrl)) {
|
||||
@@ -47,6 +48,7 @@ export async function handleFirstConnect(serverUrl: string) {
|
||||
|
||||
alreadyConnected.add(serverUrl);
|
||||
resetWebSocketLastDisconnected(operator);
|
||||
fetchStatusByIds(serverUrl, ['me']);
|
||||
}
|
||||
|
||||
export function handleReconnect(serverUrl: string) {
|
||||
|
||||
@@ -9,8 +9,7 @@ import {useIntl} from 'react-intl';
|
||||
import {StyleProp, Text, TextStyle} from 'react-native';
|
||||
import {map, switchMap} from 'rxjs/operators';
|
||||
|
||||
import {switchToChannel} from '@actions/local/channel';
|
||||
import {joinChannel} from '@actions/remote/channel';
|
||||
import {joinChannel, switchToChannelById} from '@actions/remote/channel';
|
||||
import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {t} from '@i18n';
|
||||
@@ -96,7 +95,7 @@ const ChannelMention = ({
|
||||
}
|
||||
|
||||
if (c?.id) {
|
||||
switchToChannel(serverUrl, c.id);
|
||||
switchToChannelById(serverUrl, c.id);
|
||||
await dismissAllModals();
|
||||
await popToRoot();
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ exports[`Server Icon Server Icon Component should match snapshot with unreads 1`
|
||||
"fontFamily": "OpenSans-Bold",
|
||||
"fontSize": 12,
|
||||
"height": 12,
|
||||
"left": 25,
|
||||
"left": 18,
|
||||
"lineHeight": 16.5,
|
||||
"minWidth": 12,
|
||||
"opacity": 1,
|
||||
@@ -150,7 +150,7 @@ exports[`Server Icon Server Icon Component should match snapshot with unreads 1`
|
||||
"paddingHorizontal": 0,
|
||||
"position": "absolute",
|
||||
"textAlign": "center",
|
||||
"top": 5,
|
||||
"top": -5,
|
||||
"transform": Array [
|
||||
Object {
|
||||
"scale": 1,
|
||||
|
||||
@@ -29,7 +29,8 @@ const styles = StyleSheet.create({
|
||||
left: 25,
|
||||
},
|
||||
unread: {
|
||||
top: 5,
|
||||
left: 18,
|
||||
top: -5,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -80,6 +80,9 @@ export const transformUserRecord = ({action, database, value}: TransformerArgs):
|
||||
user.props = raw.props || null;
|
||||
user.timezone = raw.timezone || null;
|
||||
user.isBot = raw.is_bot;
|
||||
if (raw.status) {
|
||||
user.status = raw.status;
|
||||
}
|
||||
};
|
||||
|
||||
return prepareBaseRecord({
|
||||
|
||||
@@ -8,7 +8,7 @@ import {Navigation as RNN} from 'react-native-navigation';
|
||||
import Animated from 'react-native-reanimated';
|
||||
import RNBottomSheet from 'reanimated-bottom-sheet';
|
||||
|
||||
import {Navigation} from '@constants';
|
||||
import {Navigation, Screens} from '@constants';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {useIsTablet} from '@hooks/device';
|
||||
import {dismissModal} from '@screens/navigation';
|
||||
@@ -36,7 +36,7 @@ const BottomSheet = ({closeButtonId, initialSnapIndex = 0, renderContent, snapPo
|
||||
if (sheetRef.current) {
|
||||
sheetRef.current.snapTo(lastSnap);
|
||||
} else {
|
||||
dismissModal();
|
||||
dismissModal({componentId: Screens.BOTTOM_SHEET});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -48,7 +48,7 @@ const BottomSheet = ({closeButtonId, initialSnapIndex = 0, renderContent, snapPo
|
||||
if (sheetRef.current) {
|
||||
sheetRef.current.snapTo(1);
|
||||
} else {
|
||||
dismissModal();
|
||||
dismissModal({componentId: Screens.BOTTOM_SHEET});
|
||||
}
|
||||
return true;
|
||||
});
|
||||
@@ -65,7 +65,7 @@ const BottomSheet = ({closeButtonId, initialSnapIndex = 0, renderContent, snapPo
|
||||
useEffect(() => {
|
||||
const navigationEvents = RNN.events().registerNavigationButtonPressedListener(({buttonId}) => {
|
||||
if (closeButtonId && buttonId === closeButtonId) {
|
||||
dismissModal();
|
||||
dismissModal({componentId: Screens.BOTTOM_SHEET});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -4,17 +4,15 @@
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
import {useRoute} from '@react-navigation/native';
|
||||
import React, {useCallback, useEffect, useState} from 'react';
|
||||
import React, {useCallback, useState} from 'react';
|
||||
import {ScrollView, View} from 'react-native';
|
||||
import Animated, {useAnimatedStyle, withTiming} from 'react-native-reanimated';
|
||||
import {Edge, SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context';
|
||||
import {of as of$} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import {fetchStatusInBatch} from '@actions/remote/user';
|
||||
import {View as ViewConstants} from '@constants';
|
||||
import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {useIsTablet} from '@hooks/device';
|
||||
import {isCustomStatusExpirySupported, isMinimumServerVersion} from '@utils/helpers';
|
||||
@@ -73,13 +71,6 @@ const AccountScreen = ({currentUser, enableCustomUserStatuses, customStatusExpir
|
||||
const route = useRoute();
|
||||
const insets = useSafeAreaInsets();
|
||||
const isTablet = useIsTablet();
|
||||
const serverUrl = useServerUrl();
|
||||
|
||||
useEffect(() => {
|
||||
if (currentUser) {
|
||||
fetchStatusInBatch(serverUrl, currentUser.id);
|
||||
}
|
||||
}, []);
|
||||
|
||||
let tabletSidebarStyle;
|
||||
if (isTablet) {
|
||||
|
||||
@@ -9,8 +9,9 @@ import {Navigation, Options, OptionsModalPresentationStyle} from 'react-native-n
|
||||
import tinyColor from 'tinycolor2';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import {Device, Preferences, Screens} from '@constants';
|
||||
import {Device, Screens} from '@constants';
|
||||
import NavigationConstants from '@constants/navigation';
|
||||
import {getDefaultThemeByAppearance} from '@context/theme';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import {NavButtons} from '@typings/screens/navigation';
|
||||
import {changeOpacity, setNavigatorStyles} from '@utils/theme';
|
||||
@@ -19,7 +20,7 @@ import type {LaunchProps} from '@typings/launch';
|
||||
|
||||
const {MattermostManaged} = NativeModules;
|
||||
const isRunningInSplitView = MattermostManaged.isRunningInSplitView;
|
||||
const appearanceControlledScreens = [Screens.SERVER, Screens.LOGIN, Screens.FORGOT_PASSWORD, Screens.MFA];
|
||||
export const appearanceControlledScreens = [Screens.SERVER, Screens.LOGIN, Screens.FORGOT_PASSWORD, Screens.MFA, Screens.SSO];
|
||||
|
||||
const alpha = {
|
||||
from: 0,
|
||||
@@ -30,6 +31,10 @@ const alpha = {
|
||||
export const loginAnimationOptions = () => {
|
||||
const theme = getThemeFromState();
|
||||
return {
|
||||
layout: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
componentBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
topBar: {
|
||||
visible: true,
|
||||
drawBehind: true,
|
||||
@@ -96,10 +101,8 @@ function getThemeFromState(): Theme {
|
||||
if (EphemeralStore.theme) {
|
||||
return EphemeralStore.theme;
|
||||
}
|
||||
if (Appearance.getColorScheme() === 'dark') {
|
||||
return Preferences.THEMES.onyx;
|
||||
}
|
||||
return Preferences.THEMES.denim;
|
||||
|
||||
return getDefaultThemeByAppearance();
|
||||
}
|
||||
|
||||
export function resetToHome(passProps = {}) {
|
||||
@@ -145,7 +148,7 @@ export function resetToHome(passProps = {}) {
|
||||
}
|
||||
|
||||
export function resetToSelectServer(passProps: LaunchProps) {
|
||||
const theme = getThemeFromState();
|
||||
const theme = getDefaultThemeByAppearance();
|
||||
const isDark = tinyColor(theme.centerChannelBg).isDark();
|
||||
StatusBar.setBarStyle(isDark ? 'light-content' : 'dark-content');
|
||||
|
||||
@@ -565,7 +568,9 @@ export async function bottomSheet({title, renderContent, snapPoints, initialSnap
|
||||
snapPoints,
|
||||
}, {
|
||||
modalPresentationStyle: OptionsModalPresentationStyle.formSheet,
|
||||
swipeToDismiss: true,
|
||||
modal: {
|
||||
swipeToDismiss: true,
|
||||
},
|
||||
topBar: {
|
||||
leftButtons: [{
|
||||
id: closeButtonId,
|
||||
@@ -586,6 +591,6 @@ export async function bottomSheet({title, renderContent, snapPoints, initialSnap
|
||||
initialSnapIndex,
|
||||
renderContent,
|
||||
snapPoints,
|
||||
}, {swipeToDismiss: true});
|
||||
}, {modal: {swipeToDismiss: true}});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import merge from 'deepmerge';
|
||||
import {StatusBar, StyleSheet} from 'react-native';
|
||||
import tinyColor from 'tinycolor2';
|
||||
|
||||
import {Preferences, Screens} from '@constants';
|
||||
import {mergeNavigationOptions} from '@screens/navigation';
|
||||
import {appearanceControlledScreens, mergeNavigationOptions} from '@screens/navigation';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
|
||||
import type {Options} from 'react-native-navigation';
|
||||
@@ -121,17 +122,14 @@ export function setNavigatorStyles(componentId: string, theme: Theme, additional
|
||||
}
|
||||
StatusBar.setBarStyle(isDark ? 'light-content' : 'dark-content');
|
||||
|
||||
const mergeOptions = {
|
||||
...options,
|
||||
...additionalOptions,
|
||||
};
|
||||
const mergeOptions = merge(options, additionalOptions);
|
||||
|
||||
mergeNavigationOptions(componentId, mergeOptions);
|
||||
}
|
||||
|
||||
export function setNavigationStackStyles(theme: Theme) {
|
||||
EphemeralStore.allNavigationComponentIds.forEach((componentId) => {
|
||||
if (componentId !== Screens.BOTTOM_SHEET) {
|
||||
if (componentId !== Screens.BOTTOM_SHEET && !appearanceControlledScreens.includes(componentId)) {
|
||||
setNavigatorStyles(componentId, theme);
|
||||
}
|
||||
});
|
||||
|
||||
1
types/api/users.d.ts
vendored
1
types/api/users.d.ts
vendored
@@ -41,6 +41,7 @@ type UserProfile = {
|
||||
timezone?: UserTimezone;
|
||||
is_bot: boolean;
|
||||
last_picture_update: number;
|
||||
status?: string;
|
||||
};
|
||||
|
||||
type UsersState = {
|
||||
|
||||
Reference in New Issue
Block a user