[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:
Elias Nahum
2022-02-10 11:46:35 -03:00
committed by GitHub
parent 210a2f2d8a
commit 1b62c10dcc
14 changed files with 80 additions and 53 deletions

View File

@@ -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) {

View File

@@ -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

View File

@@ -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[]);

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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();
}

View File

@@ -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,

View File

@@ -29,7 +29,8 @@ const styles = StyleSheet.create({
left: 25,
},
unread: {
top: 5,
left: 18,
top: -5,
},
});

View File

@@ -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({

View File

@@ -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});
}
});

View File

@@ -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) {

View File

@@ -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}});
}
}

View File

@@ -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);
}
});

View File

@@ -41,6 +41,7 @@ type UserProfile = {
timezone?: UserTimezone;
is_bot: boolean;
last_picture_update: number;
status?: string;
};
type UsersState = {