forked from Ivasoft/mattermost-mobile
[Gekidou] cleanup fixes (#6376)
* Separate NavigationStore from EphemeralStore * Prevent WS to mark channel as read while switching to the channel * Fix mark channel as unread
This commit is contained in:
@@ -6,6 +6,7 @@ import {DeviceEventEmitter} from 'react-native';
|
||||
|
||||
import {General, Navigation as NavigationConstants, Preferences, Screens} from '@constants';
|
||||
import {SYSTEM_IDENTIFIERS} from '@constants/database';
|
||||
import {getDefaultThemeByAppearance} from '@context/theme';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {getTeammateNameDisplaySetting} from '@helpers/api/preference';
|
||||
import {extractChannelDisplayName} from '@helpers/database';
|
||||
@@ -41,6 +42,8 @@ export async function switchToChannel(serverUrl: string, channelId: string, team
|
||||
const system = await getCommonSystemValues(database);
|
||||
const member = await getMyChannel(database, channelId);
|
||||
|
||||
EphemeralStore.addSwitchingToChannel(channelId);
|
||||
|
||||
if (member) {
|
||||
const channel = await member.channel.fetch();
|
||||
if (channel) {
|
||||
@@ -93,7 +96,7 @@ export async function switchToChannel(serverUrl: string, channelId: string, team
|
||||
// causing the goToScreen to use the Appearance theme instead and that causes the screen background color to potentially
|
||||
// not match the theme
|
||||
const themes = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_THEME, toTeamId).fetch();
|
||||
let theme = Preferences.THEMES.denim;
|
||||
let theme = getDefaultThemeByAppearance();
|
||||
if (themes.length) {
|
||||
theme = setThemeDefaults(JSON.parse(themes[0].value) as Theme);
|
||||
}
|
||||
@@ -231,7 +234,7 @@ export async function markChannelAsUnread(serverUrl: string, channelId: string,
|
||||
|
||||
member.prepareUpdate((m) => {
|
||||
m.viewedAt = lastViewed - 1;
|
||||
m.lastViewedAt = lastViewed;
|
||||
m.lastViewedAt = lastViewed - 1;
|
||||
m.messageCount = messageCount;
|
||||
m.mentionsCount = mentionsCount;
|
||||
m.manuallyUnread = true;
|
||||
|
||||
@@ -16,6 +16,7 @@ import {getIsCRTEnabled, getThreadById, prepareThreadsFromReceivedPosts, queryTh
|
||||
import {getCurrentUser} from '@queries/servers/user';
|
||||
import {dismissAllModalsAndPopToRoot, goToScreen} from '@screens/navigation';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
import {isTablet} from '@utils/helpers';
|
||||
import {changeOpacity, setThemeDefaults, updateThemeIfNeeded} from '@utils/theme';
|
||||
|
||||
@@ -155,7 +156,7 @@ export const switchToThread = async (serverUrl: string, rootId: string, isFromNo
|
||||
|
||||
if (isFromNotification) {
|
||||
await dismissAllModalsAndPopToRoot();
|
||||
await EphemeralStore.waitUntilScreenIsTop(Screens.HOME);
|
||||
await NavigationStore.waitUntilScreenIsTop(Screens.HOME);
|
||||
if (switchingTeams && isTabletDevice) {
|
||||
DeviceEventEmitter.emit(Navigation.NAVIGATION_HOME, Screens.GLOBAL_THREADS);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import {getCommonSystemValues, getCurrentTeamId, getWebSocketLastDisconnected, s
|
||||
import {getMyTeamById} from '@queries/servers/team';
|
||||
import {getIsCRTEnabled} from '@queries/servers/thread';
|
||||
import {getCurrentUser} from '@queries/servers/user';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
import {isTablet} from '@utils/helpers';
|
||||
import {emitNotificationError} from '@utils/notification';
|
||||
|
||||
@@ -51,7 +51,7 @@ export async function pushNotificationEntry(serverUrl: string, notification: Not
|
||||
const isCRTEnabled = await getIsCRTEnabled(database);
|
||||
const isThreadNotification = isCRTEnabled && Boolean(rootId);
|
||||
|
||||
await EphemeralStore.waitUntilScreenHasLoaded(Screens.HOME);
|
||||
await NavigationStore.waitUntilScreenHasLoaded(Screens.HOME);
|
||||
|
||||
let switchedToScreen = false;
|
||||
let switchedToChannel = false;
|
||||
|
||||
@@ -130,8 +130,7 @@ export async function handleChannelViewedEvent(serverUrl: string, msg: any) {
|
||||
const activeServerUrl = await DatabaseManager.getActiveServerUrl();
|
||||
const currentChannelId = await getCurrentChannelId(database);
|
||||
|
||||
const viewingChannel = currentChannelId === channelId && EphemeralStore.getNavigationComponents().includes(Screens.CHANNEL);
|
||||
if (activeServerUrl !== serverUrl || currentChannelId !== channelId || !viewingChannel) {
|
||||
if (activeServerUrl !== serverUrl || (currentChannelId !== channelId && !EphemeralStore.isSwitchingToChannel(channelId))) {
|
||||
await markChannelAsViewed(serverUrl, channelId, false);
|
||||
}
|
||||
} catch {
|
||||
|
||||
@@ -15,7 +15,7 @@ import {getCommonSystemValues, getConfig, getWebSocketLastDisconnected, resetWeb
|
||||
import {getCurrentTeam} from '@queries/servers/team';
|
||||
import {getCurrentUser} from '@queries/servers/user';
|
||||
import {dismissAllModals, popToRoot} from '@screens/navigation';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
import {isTablet} from '@utils/helpers';
|
||||
|
||||
import {handleCategoryCreatedEvent, handleCategoryDeletedEvent, handleCategoryOrderUpdatedEvent, handleCategoryUpdatedEvent} from './category';
|
||||
@@ -117,7 +117,7 @@ async function doReconnect(serverUrl: string) {
|
||||
// if no longer a member of the current team or the current channel
|
||||
if (initialTeamId !== currentTeam?.id || initialChannelId !== currentChannel?.id) {
|
||||
const currentServer = await queryActiveServer(appDatabase);
|
||||
const isChannelScreenMounted = EphemeralStore.getNavigationComponents().includes(Screens.CHANNEL);
|
||||
const isChannelScreenMounted = NavigationStore.getNavigationComponents().includes(Screens.CHANNEL);
|
||||
if (serverUrl === currentServer?.url) {
|
||||
if (currentTeam && initialTeamId !== currentTeam.id) {
|
||||
DeviceEventEmitter.emit(Events.LEAVE_TEAM, {displayName: currentTeam.displayName});
|
||||
|
||||
@@ -16,7 +16,7 @@ import {getChannelById, getMyChannel} from '@queries/servers/channel';
|
||||
import {getPostById} from '@queries/servers/post';
|
||||
import {getCurrentChannelId, getCurrentTeamId, getCurrentUserId} from '@queries/servers/system';
|
||||
import {getIsCRTEnabled} from '@queries/servers/thread';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
import {isTablet} from '@utils/helpers';
|
||||
import {isFromWebhook, isSystemMessage, shouldIgnorePost} from '@utils/post';
|
||||
|
||||
@@ -141,7 +141,7 @@ export async function handleNewPostEvent(serverUrl: string, msg: WebSocketMessag
|
||||
// Don't mark as read if we're in global threads screen
|
||||
// the currentChannelId still refers to previously viewed channel
|
||||
|
||||
const isChannelScreenMounted = EphemeralStore.getNavigationComponents().includes(Screens.CHANNEL);
|
||||
const isChannelScreenMounted = NavigationStore.getNavigationComponents().includes(Screens.CHANNEL);
|
||||
|
||||
const isTabletDevice = await isTablet();
|
||||
if (isChannelScreenMounted || isTabletDevice) {
|
||||
|
||||
@@ -19,7 +19,7 @@ import {useTheme} from '@context/theme';
|
||||
import {useIsTablet} from '@hooks/device';
|
||||
import useDidUpdate from '@hooks/did_update';
|
||||
import {t} from '@i18n';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
import {extractFileInfo} from '@utils/file';
|
||||
import {switchKeyboardForCodeBlocks} from '@utils/markdown';
|
||||
import {changeOpacity, makeStyleSheetFromTheme, getKeyboardAppearanceFromTheme} from '@utils/theme';
|
||||
@@ -219,7 +219,7 @@ export default function PostInput({
|
||||
}, [addFiles, intl]);
|
||||
|
||||
const handleHardwareEnterPress = useCallback((keyEvent: {pressedKey: string}) => {
|
||||
const topScreen = EphemeralStore.getNavigationTopComponentId();
|
||||
const topScreen = NavigationStore.getNavigationTopComponentId();
|
||||
let sourceScreen = Screens.CHANNEL;
|
||||
if (rootId) {
|
||||
sourceScreen = Screens.THREAD;
|
||||
|
||||
@@ -27,6 +27,7 @@ import {getCurrentChannelId} from '@queries/servers/system';
|
||||
import {getIsCRTEnabled} from '@queries/servers/thread';
|
||||
import {showOverlay} from '@screens/navigation';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
import {isTablet} from '@utils/helpers';
|
||||
import {convertToNotificationData} from '@utils/notification';
|
||||
|
||||
@@ -147,10 +148,10 @@ class PushNotifications {
|
||||
}
|
||||
|
||||
const isDifferentChannel = payload?.channel_id !== channelId;
|
||||
const isVisibleThread = payload?.root_id === EphemeralStore.getLastViewedThreadId() && EphemeralStore.getNavigationTopComponentId() === Screens.THREAD;
|
||||
let isChannelScreenVisible = EphemeralStore.getNavigationTopComponentId() === Screens.CHANNEL;
|
||||
const isVisibleThread = payload?.root_id === EphemeralStore.getLastViewedThreadId() && NavigationStore.getNavigationTopComponentId() === Screens.THREAD;
|
||||
let isChannelScreenVisible = NavigationStore.getNavigationTopComponentId() === Screens.CHANNEL;
|
||||
if (isTabletDevice) {
|
||||
isChannelScreenVisible = EphemeralStore.getVisibleTab() === Screens.HOME;
|
||||
isChannelScreenVisible = NavigationStore.getVisibleTab() === Screens.HOME;
|
||||
}
|
||||
|
||||
if (isDifferentChannel || (!isChannelScreenVisible && !isVisibleThread)) {
|
||||
|
||||
@@ -12,7 +12,7 @@ import {Events} from '@constants';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {useIsTablet} from '@hooks/device';
|
||||
import {dismissModal} from '@screens/navigation';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
import {hapticFeedback} from '@utils/general';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
@@ -56,7 +56,7 @@ const BottomSheet = ({closeButtonId, componentId, initialSnapIndex = 0, renderCo
|
||||
|
||||
useEffect(() => {
|
||||
const listener = BackHandler.addEventListener('hardwareBackPress', () => {
|
||||
if (EphemeralStore.getNavigationTopComponentId() === componentId) {
|
||||
if (NavigationStore.getNavigationTopComponentId() === componentId) {
|
||||
if (sheetRef.current) {
|
||||
sheetRef.current.snapTo(1);
|
||||
} else {
|
||||
|
||||
@@ -15,6 +15,7 @@ import {useDefaultHeaderHeight} from '@hooks/header';
|
||||
import {useTeamSwitch} from '@hooks/team_switch';
|
||||
import {popTopScreen} from '@screens/navigation';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
|
||||
import ChannelPostList from './channel_post_list';
|
||||
import ChannelHeader from './header';
|
||||
@@ -58,7 +59,7 @@ const Channel = ({channelId, componentId}: ChannelProps) => {
|
||||
let back: NativeEventSubscription|undefined;
|
||||
if (!isTablet && componentId) {
|
||||
back = BackHandler.addEventListener('hardwareBackPress', () => {
|
||||
if (EphemeralStore.getNavigationTopComponentId() === componentId) {
|
||||
if (NavigationStore.getNavigationTopComponentId() === componentId) {
|
||||
popTopScreen(componentId);
|
||||
return true;
|
||||
}
|
||||
@@ -74,11 +75,20 @@ const Channel = ({channelId, componentId}: ChannelProps) => {
|
||||
useEffect(() => {
|
||||
// This is done so that the header renders
|
||||
// and the screen does not look totally blank
|
||||
const t = requestAnimationFrame(() => {
|
||||
const raf = requestAnimationFrame(() => {
|
||||
setShouldRenderPosts(Boolean(channelId));
|
||||
});
|
||||
|
||||
return () => cancelAnimationFrame(t);
|
||||
// This is done to give time to the WS event
|
||||
const t = setTimeout(() => {
|
||||
EphemeralStore.removeSwitchingToChannel(channelId);
|
||||
}, 500);
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(raf);
|
||||
clearTimeout(t);
|
||||
EphemeralStore.removeSwitchingToChannel(channelId);
|
||||
};
|
||||
}, [channelId]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -23,7 +23,7 @@ import {withTheme} from '@context/theme';
|
||||
import {observeConfig, observeRecentCustomStatus} from '@queries/servers/system';
|
||||
import {observeCurrentUser} from '@queries/servers/user';
|
||||
import {dismissModal, goToScreen, showModal} from '@screens/navigation';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
import {getCurrentMomentForTimezone, getRoundedTime, isCustomStatusExpirySupported} from '@utils/helpers';
|
||||
import {mergeNavigationOptions} from '@utils/navigation';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
@@ -166,7 +166,7 @@ class CustomStatusModal extends NavigationComponent<Props, State> {
|
||||
|
||||
onBackPress = () => {
|
||||
const {componentId} = this.props;
|
||||
if (EphemeralStore.getNavigationTopComponentId() === componentId) {
|
||||
if (NavigationStore.getNavigationTopComponentId() === componentId) {
|
||||
if (this.props.isTablet) {
|
||||
DeviceEventEmitter.emit(Events.ACCOUNT_SELECT_TABLET_VIEW, '');
|
||||
} else {
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
import {CustomStatusDuration} from '@constants/custom_status';
|
||||
import {observeCurrentUser} from '@queries/servers/user';
|
||||
import {dismissModal, popTopScreen} from '@screens/navigation';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
import {mergeNavigationOptions} from '@utils/navigation';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
@@ -113,7 +113,7 @@ class ClearAfterModal extends NavigationComponent<Props, State> {
|
||||
|
||||
onBackPress = () => {
|
||||
const {componentId} = this.props;
|
||||
if (EphemeralStore.getNavigationTopComponentId() === componentId) {
|
||||
if (NavigationStore.getNavigationTopComponentId() === componentId) {
|
||||
if (this.props.isModal) {
|
||||
dismissModal({componentId});
|
||||
} else {
|
||||
|
||||
@@ -16,7 +16,7 @@ import {Events} from '@constants';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {dismissModal, popTopScreen, setButtons} from '@screens/navigation';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
|
||||
import ProfileForm from './components/form';
|
||||
@@ -106,7 +106,7 @@ const EditProfile = ({
|
||||
|
||||
useEffect(() => {
|
||||
const backHandler = BackHandler.addEventListener('hardwareBackPress', () => {
|
||||
if (EphemeralStore.getNavigationTopComponentId() === componentId) {
|
||||
if (NavigationStore.getNavigationTopComponentId() === componentId) {
|
||||
close();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import {Navigation as NavigationConstants, Screens} from '@constants';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {useIsTablet} from '@hooks/device';
|
||||
import {resetToTeams} from '@screens/navigation';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
|
||||
import AdditionalTabletView from './additional_tablet_view';
|
||||
import CategoriesList from './categories_list';
|
||||
@@ -54,8 +54,8 @@ const ChannelListScreen = (props: ChannelProps) => {
|
||||
const canAddOtherServers = managedConfig?.allowOtherServers !== 'false';
|
||||
|
||||
const handleBackPress = useCallback(() => {
|
||||
const isHomeScreen = EphemeralStore.getNavigationTopComponentId() === Screens.HOME;
|
||||
const homeTab = EphemeralStore.getVisibleTab() === Screens.HOME;
|
||||
const isHomeScreen = NavigationStore.getNavigationTopComponentId() === Screens.HOME;
|
||||
const homeTab = NavigationStore.getVisibleTab() === Screens.HOME;
|
||||
const focused = navigation.isFocused() && isHomeScreen && homeTab;
|
||||
if (!backPressedCount && focused) {
|
||||
backPressedCount++;
|
||||
|
||||
@@ -12,7 +12,7 @@ import {enableFreeze, enableScreens} from 'react-native-screens';
|
||||
import {Events, Screens} from '@constants';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {findChannels} from '@screens/navigation';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
import {alertChannelArchived, alertChannelRemove, alertTeamRemove} from '@utils/navigation';
|
||||
import {notificationError} from '@utils/notification';
|
||||
|
||||
@@ -76,7 +76,7 @@ export default function HomeScreen(props: HomeProps) {
|
||||
|
||||
useEffect(() => {
|
||||
const listener = HWKeyboardEvent.onHWKeyPressed((keyEvent: {pressedKey: string}) => {
|
||||
const screen = EphemeralStore.getAllNavigationComponents();
|
||||
const screen = NavigationStore.getAllNavigationComponents();
|
||||
if (!screen.includes(Screens.FIND_CHANNELS) && keyEvent.pressedKey === 'find-channels') {
|
||||
findChannels(
|
||||
intl.formatMessage({id: 'find_channels.title', defaultMessage: 'Find Channels'}),
|
||||
|
||||
@@ -8,7 +8,7 @@ import Animated, {useAnimatedStyle, withTiming} from 'react-native-reanimated';
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context';
|
||||
|
||||
import {Events, Navigation as NavigationConstants, Screens, View as ViewConstants} from '@constants';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import Account from './account';
|
||||
@@ -77,7 +77,7 @@ function TabBar({state, descriptors, navigation, theme}: BottomTabBarProps & {th
|
||||
|
||||
useEffect(() => {
|
||||
const listner = DeviceEventEmitter.addListener(NavigationConstants.NAVIGATION_HOME, () => {
|
||||
EphemeralStore.setVisibleTap(Screens.HOME);
|
||||
NavigationStore.setVisibleTap(Screens.HOME);
|
||||
navigation.navigate(Screens.HOME);
|
||||
});
|
||||
|
||||
@@ -102,7 +102,7 @@ function TabBar({state, descriptors, navigation, theme}: BottomTabBarProps & {th
|
||||
if (!event.defaultPrevented) {
|
||||
// The `merge: true` option makes sure that the params inside the tab screen are preserved
|
||||
navigation.navigate({params: {direction, ...params}, name: route.name, merge: false});
|
||||
EphemeralStore.setVisibleTap(route.name);
|
||||
NavigationStore.setVisibleTap(route.name);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -168,7 +168,7 @@ function TabBar({state, descriptors, navigation, theme}: BottomTabBarProps & {th
|
||||
if (!isFocused && !event.defaultPrevented) {
|
||||
// The `merge: true` option makes sure that the params inside the tab screen are preserved
|
||||
navigation.navigate({params: {direction}, name: route.name, merge: false});
|
||||
EphemeralStore.setVisibleTap(route.name);
|
||||
NavigationStore.setVisibleTap(route.name);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import NavigationConstants from '@constants/navigation';
|
||||
import {NOT_READY} from '@constants/screens';
|
||||
import {getDefaultThemeByAppearance} from '@context/theme';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
import {LaunchProps, LaunchType} from '@typings/launch';
|
||||
import {appearanceControlledScreens, mergeNavigationOptions} from '@utils/navigation';
|
||||
import {changeOpacity, setNavigatorStyles} from '@utils/theme';
|
||||
@@ -149,7 +150,7 @@ Navigation.setDefaultOptions({
|
||||
|
||||
Appearance.addChangeListener(() => {
|
||||
const theme = getThemeFromState();
|
||||
const screens = EphemeralStore.getAllNavigationComponents();
|
||||
const screens = NavigationStore.getAllNavigationComponents();
|
||||
|
||||
if (screens.includes(Screens.SERVER)) {
|
||||
for (const screen of screens) {
|
||||
@@ -198,7 +199,7 @@ export function resetToHome(passProps: LaunchProps = {launchType: LaunchType.Nor
|
||||
return;
|
||||
}
|
||||
|
||||
EphemeralStore.clearNavigationComponents();
|
||||
NavigationStore.clearNavigationComponents();
|
||||
|
||||
const stack = {
|
||||
children: [{
|
||||
@@ -240,7 +241,7 @@ export function resetToSelectServer(passProps: LaunchProps) {
|
||||
const isDark = tinyColor(theme.sidebarBg).isDark();
|
||||
StatusBar.setBarStyle(isDark ? 'light-content' : 'dark-content');
|
||||
|
||||
EphemeralStore.clearNavigationComponents();
|
||||
NavigationStore.clearNavigationComponents();
|
||||
|
||||
const children = [{
|
||||
component: {
|
||||
@@ -288,7 +289,7 @@ export function resetToTeams() {
|
||||
const isDark = tinyColor(theme.sidebarBg).isDark();
|
||||
StatusBar.setBarStyle(isDark ? 'light-content' : 'dark-content');
|
||||
|
||||
EphemeralStore.clearNavigationComponents();
|
||||
NavigationStore.clearNavigationComponents();
|
||||
|
||||
Navigation.setRoot({
|
||||
root: {
|
||||
@@ -331,7 +332,7 @@ export function goToScreen(name: string, title: string, passProps = {}, options
|
||||
|
||||
const theme = getThemeFromState();
|
||||
const isDark = tinyColor(theme.sidebarBg).isDark();
|
||||
const componentId = EphemeralStore.getNavigationTopComponentId();
|
||||
const componentId = NavigationStore.getNavigationTopComponentId();
|
||||
DeviceEventEmitter.emit(Events.TAB_BAR_VISIBLE, false);
|
||||
const defaultOptions: Options = {
|
||||
layout: {
|
||||
@@ -378,13 +379,13 @@ export function popTopScreen(screenId?: string) {
|
||||
if (screenId) {
|
||||
Navigation.pop(screenId);
|
||||
} else {
|
||||
const componentId = EphemeralStore.getNavigationTopComponentId();
|
||||
const componentId = NavigationStore.getNavigationTopComponentId();
|
||||
Navigation.pop(componentId);
|
||||
}
|
||||
}
|
||||
|
||||
export async function popToRoot() {
|
||||
const componentId = EphemeralStore.getNavigationTopComponentId();
|
||||
const componentId = NavigationStore.getNavigationTopComponentId();
|
||||
|
||||
try {
|
||||
await Navigation.popToRoot(componentId);
|
||||
@@ -411,7 +412,7 @@ export async function dismissAllModalsAndPopToRoot() {
|
||||
*/
|
||||
export async function dismissAllModalsAndPopToScreen(screenId: string, title: string, passProps = {}, options = {}) {
|
||||
await dismissAllModals();
|
||||
if (EphemeralStore.getNavigationComponents().includes(screenId)) {
|
||||
if (NavigationStore.getNavigationComponents().includes(screenId)) {
|
||||
let mergeOptions = options;
|
||||
if (title) {
|
||||
mergeOptions = merge(mergeOptions, {
|
||||
@@ -466,7 +467,7 @@ export function showModal(name: string, title: string, passProps = {}, options =
|
||||
},
|
||||
};
|
||||
|
||||
EphemeralStore.addNavigationModal(name);
|
||||
NavigationStore.addNavigationModal(name);
|
||||
Navigation.showModal({
|
||||
stack: {
|
||||
children: [{
|
||||
@@ -563,15 +564,15 @@ export function showSearchModal(initialValue = '') {
|
||||
}
|
||||
|
||||
export async function dismissModal(options?: Options & { componentId: string}) {
|
||||
if (!EphemeralStore.hasModalsOpened()) {
|
||||
if (!NavigationStore.hasModalsOpened()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const componentId = options?.componentId || EphemeralStore.getNavigationTopModalId();
|
||||
const componentId = options?.componentId || NavigationStore.getNavigationTopModalId();
|
||||
if (componentId) {
|
||||
try {
|
||||
await Navigation.dismissModal(componentId, options);
|
||||
EphemeralStore.removeNavigationModal(componentId);
|
||||
NavigationStore.removeNavigationModal(componentId);
|
||||
} catch (error) {
|
||||
// RNN returns a promise rejection if there is no modal to
|
||||
// dismiss. We'll do nothing in this case.
|
||||
@@ -580,14 +581,14 @@ export async function dismissModal(options?: Options & { componentId: string}) {
|
||||
}
|
||||
|
||||
export async function dismissAllModals() {
|
||||
if (!EphemeralStore.hasModalsOpened()) {
|
||||
if (!NavigationStore.hasModalsOpened()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const modals = [...EphemeralStore.getAllNavigationModals()];
|
||||
const modals = [...NavigationStore.getAllNavigationModals()];
|
||||
for await (const modal of modals) {
|
||||
EphemeralStore.removeNavigationModal(modal);
|
||||
NavigationStore.removeNavigationModal(modal);
|
||||
await Navigation.dismissModal(modal, {animations: {dismissModal: {enabled: false}}});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -685,7 +686,7 @@ export async function bottomSheet({title, renderContent, snapPoints, initialSnap
|
||||
|
||||
export async function dismissBottomSheet(alternativeScreen = Screens.BOTTOM_SHEET) {
|
||||
DeviceEventEmitter.emit(Events.CLOSE_BOTTOM_SHEET);
|
||||
await EphemeralStore.waitUntilScreensIsRemoved(alternativeScreen);
|
||||
await NavigationStore.waitUntilScreensIsRemoved(alternativeScreen);
|
||||
}
|
||||
|
||||
type AsBottomSheetArgs = {
|
||||
|
||||
@@ -17,7 +17,7 @@ import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {useIsTablet} from '@hooks/device';
|
||||
import {dismissModal} from '@screens/navigation';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles';
|
||||
import {closePermalink} from '@utils/permalink';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
@@ -152,7 +152,7 @@ function Permalink({channel, isCRTEnabled, postId}: Props) {
|
||||
|
||||
useEffect(() => {
|
||||
const listener = BackHandler.addEventListener('hardwareBackPress', () => {
|
||||
if (EphemeralStore.getNavigationTopComponentId() === Screens.PERMALINK) {
|
||||
if (NavigationStore.getNavigationTopComponentId() === Screens.PERMALINK) {
|
||||
handleClose();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import {Events, Screens} from '@constants';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {popTopScreen} from '@screens/navigation';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
import {isDateLine, getDateForDateLine, selectOrderedPosts} from '@utils/post_list';
|
||||
|
||||
import EmptyState from './empty';
|
||||
@@ -80,7 +80,7 @@ function SavedMessages({
|
||||
|
||||
useEffect(() => {
|
||||
const listener = BackHandler.addEventListener('hardwareBackPress', () => {
|
||||
if (EphemeralStore.getNavigationTopComponentId() === componentId) {
|
||||
if (NavigationStore.getNavigationTopComponentId() === componentId) {
|
||||
close();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import {Screens} from '@constants';
|
||||
import {useServerDisplayName} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {dismissModal, goToScreen, setButtons} from '@screens/navigation';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
@@ -95,7 +95,7 @@ const Settings = ({componentId, showHelp, siteName}: SettingsProps) => {
|
||||
|
||||
useEffect(() => {
|
||||
const backHandler = BackHandler.addEventListener('hardwareBackPress', () => {
|
||||
if (EphemeralStore.getNavigationTopComponentId() === componentId) {
|
||||
if (NavigationStore.getNavigationTopComponentId() === componentId) {
|
||||
close();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2,11 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
class EphemeralStore {
|
||||
allNavigationComponentIds: string[] = [];
|
||||
navigationComponentIdStack: string[] = [];
|
||||
navigationModalStack: string[] = [];
|
||||
theme: Theme | undefined;
|
||||
visibleTab = 'Home';
|
||||
creatingChannel = false;
|
||||
creatingDMorGMTeammates: string[] = [];
|
||||
|
||||
@@ -21,140 +17,9 @@ class EphemeralStore {
|
||||
private leavingChannels = new Set<string>();
|
||||
private archivingChannels = new Set<string>();
|
||||
private convertingChannels = new Set<string>();
|
||||
private switchingToChannel = new Set<string>();
|
||||
private lastViewedThreadId = '';
|
||||
|
||||
addNavigationComponentId = (componentId: string) => {
|
||||
this.addToNavigationComponentIdStack(componentId);
|
||||
this.addToAllNavigationComponentIds(componentId);
|
||||
};
|
||||
|
||||
addToAllNavigationComponentIds = (componentId: string) => {
|
||||
if (!this.allNavigationComponentIds.includes(componentId)) {
|
||||
this.allNavigationComponentIds.unshift(componentId);
|
||||
}
|
||||
};
|
||||
|
||||
addToNavigationComponentIdStack = (componentId: string) => {
|
||||
const index = this.navigationComponentIdStack.indexOf(componentId);
|
||||
if (index >= 0) {
|
||||
this.navigationComponentIdStack.splice(index, 1);
|
||||
}
|
||||
|
||||
this.navigationComponentIdStack.unshift(componentId);
|
||||
};
|
||||
|
||||
addNavigationModal = (componentId: string) => {
|
||||
this.navigationModalStack.unshift(componentId);
|
||||
};
|
||||
|
||||
clearNavigationComponents = () => {
|
||||
this.navigationComponentIdStack = [];
|
||||
this.navigationModalStack = [];
|
||||
this.allNavigationComponentIds = [];
|
||||
};
|
||||
|
||||
clearNavigationModals = () => {
|
||||
this.navigationModalStack = [];
|
||||
};
|
||||
|
||||
getAllNavigationComponents = () => this.allNavigationComponentIds;
|
||||
|
||||
getAllNavigationModals = () => this.navigationModalStack;
|
||||
|
||||
getNavigationTopComponentId = () => {
|
||||
return this.navigationComponentIdStack[0];
|
||||
};
|
||||
|
||||
getNavigationTopModalId = () => {
|
||||
return this.navigationModalStack[0];
|
||||
};
|
||||
|
||||
getNavigationComponents = () => {
|
||||
return this.navigationComponentIdStack;
|
||||
};
|
||||
|
||||
getVisibleTab = () => this.visibleTab;
|
||||
|
||||
hasModalsOpened = () => this.navigationModalStack.length > 0;
|
||||
|
||||
private removeNavigationComponent = (componentId: string) => {
|
||||
const index = this.allNavigationComponentIds.indexOf(componentId);
|
||||
if (index >= 0) {
|
||||
this.allNavigationComponentIds.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
removeNavigationComponentId = (componentId: string) => {
|
||||
this.removeNavigationComponent(componentId);
|
||||
const index = this.navigationComponentIdStack.indexOf(componentId);
|
||||
if (index >= 0) {
|
||||
this.navigationComponentIdStack.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
removeNavigationModal = (componentId: string) => {
|
||||
this.removeNavigationComponentId(componentId);
|
||||
const index = this.navigationModalStack.indexOf(componentId);
|
||||
|
||||
if (index >= 0) {
|
||||
this.navigationModalStack.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
setVisibleTap = (tab: string) => {
|
||||
this.visibleTab = tab;
|
||||
};
|
||||
|
||||
/**
|
||||
* Waits until a screen has been mounted and is part of the stack.
|
||||
* Use this function only if you know what you are doing
|
||||
* this function will run until the screen appears in the stack
|
||||
* and can easily run forever if the screen is never prevesented.
|
||||
* @param componentId string
|
||||
*/
|
||||
waitUntilScreenHasLoaded = async (componentId: string) => {
|
||||
let found = false;
|
||||
while (!found) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await (new Promise((r) => requestAnimationFrame(r)));
|
||||
|
||||
found = this.navigationComponentIdStack.includes(componentId);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Waits until a passed screen is the top screen
|
||||
* Use this function only if you know what you are doing
|
||||
* this function will run until the screen is in the top
|
||||
* @param componentId string
|
||||
*/
|
||||
waitUntilScreenIsTop = async (componentId: string) => {
|
||||
let found = false;
|
||||
while (!found) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await (new Promise((r) => requestAnimationFrame(r)));
|
||||
|
||||
found = this.getNavigationTopComponentId() === componentId;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Waits until a screen has been removed as part of the stack.
|
||||
* Use this function only if you know what you are doing
|
||||
* this function will run until the screen disappears from the stack
|
||||
* and can easily run forever if the screen is never removed.
|
||||
* @param componentId string
|
||||
*/
|
||||
waitUntilScreensIsRemoved = async (componentId: string) => {
|
||||
let found = false;
|
||||
while (!found) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await (new Promise((r) => requestAnimationFrame(r)));
|
||||
|
||||
found = !this.navigationComponentIdStack.includes(componentId);
|
||||
}
|
||||
};
|
||||
|
||||
// Ephemeral control when (un)archiving a channel locally
|
||||
addArchivingChannel = (channelId: string) => {
|
||||
this.archivingChannels.add(channelId);
|
||||
@@ -237,6 +102,19 @@ class EphemeralStore {
|
||||
setLastViewedThreadId = (id: string) => {
|
||||
this.lastViewedThreadId = id;
|
||||
};
|
||||
|
||||
// Ephemeral control when (un)archiving a channel locally
|
||||
addSwitchingToChannel = (channelId: string) => {
|
||||
this.switchingToChannel.add(channelId);
|
||||
};
|
||||
|
||||
isSwitchingToChannel = (channelId: string) => {
|
||||
return this.switchingToChannel.has(channelId);
|
||||
};
|
||||
|
||||
removeSwitchingToChannel = (channelId: string) => {
|
||||
this.switchingToChannel.delete(channelId);
|
||||
};
|
||||
}
|
||||
|
||||
export default new EphemeralStore();
|
||||
|
||||
143
app/store/navigation_store.ts
Normal file
143
app/store/navigation_store.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
class NavigationStore {
|
||||
allNavigationComponentIds: string[] = [];
|
||||
navigationComponentIdStack: string[] = [];
|
||||
navigationModalStack: string[] = [];
|
||||
visibleTab = 'Home';
|
||||
|
||||
addNavigationComponentId = (componentId: string) => {
|
||||
this.addToNavigationComponentIdStack(componentId);
|
||||
this.addToAllNavigationComponentIds(componentId);
|
||||
};
|
||||
|
||||
addToAllNavigationComponentIds = (componentId: string) => {
|
||||
if (!this.allNavigationComponentIds.includes(componentId)) {
|
||||
this.allNavigationComponentIds.unshift(componentId);
|
||||
}
|
||||
};
|
||||
|
||||
addToNavigationComponentIdStack = (componentId: string) => {
|
||||
const index = this.navigationComponentIdStack.indexOf(componentId);
|
||||
if (index >= 0) {
|
||||
this.navigationComponentIdStack.splice(index, 1);
|
||||
}
|
||||
|
||||
this.navigationComponentIdStack.unshift(componentId);
|
||||
};
|
||||
|
||||
addNavigationModal = (componentId: string) => {
|
||||
this.navigationModalStack.unshift(componentId);
|
||||
};
|
||||
|
||||
clearNavigationComponents = () => {
|
||||
this.navigationComponentIdStack = [];
|
||||
this.navigationModalStack = [];
|
||||
this.allNavigationComponentIds = [];
|
||||
};
|
||||
|
||||
clearNavigationModals = () => {
|
||||
this.navigationModalStack = [];
|
||||
};
|
||||
|
||||
getAllNavigationComponents = () => this.allNavigationComponentIds;
|
||||
|
||||
getAllNavigationModals = () => this.navigationModalStack;
|
||||
|
||||
getNavigationTopComponentId = () => {
|
||||
return this.navigationComponentIdStack[0];
|
||||
};
|
||||
|
||||
getNavigationTopModalId = () => {
|
||||
return this.navigationModalStack[0];
|
||||
};
|
||||
|
||||
getNavigationComponents = () => {
|
||||
return this.navigationComponentIdStack;
|
||||
};
|
||||
|
||||
getVisibleTab = () => this.visibleTab;
|
||||
|
||||
hasModalsOpened = () => this.navigationModalStack.length > 0;
|
||||
|
||||
private removeNavigationComponent = (componentId: string) => {
|
||||
const index = this.allNavigationComponentIds.indexOf(componentId);
|
||||
if (index >= 0) {
|
||||
this.allNavigationComponentIds.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
removeNavigationComponentId = (componentId: string) => {
|
||||
this.removeNavigationComponent(componentId);
|
||||
const index = this.navigationComponentIdStack.indexOf(componentId);
|
||||
if (index >= 0) {
|
||||
this.navigationComponentIdStack.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
removeNavigationModal = (componentId: string) => {
|
||||
this.removeNavigationComponentId(componentId);
|
||||
const index = this.navigationModalStack.indexOf(componentId);
|
||||
|
||||
if (index >= 0) {
|
||||
this.navigationModalStack.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
setVisibleTap = (tab: string) => {
|
||||
this.visibleTab = tab;
|
||||
};
|
||||
|
||||
/**
|
||||
* Waits until a screen has been mounted and is part of the stack.
|
||||
* Use this function only if you know what you are doing
|
||||
* this function will run until the screen appears in the stack
|
||||
* and can easily run forever if the screen is never prevesented.
|
||||
* @param componentId string
|
||||
*/
|
||||
waitUntilScreenHasLoaded = async (componentId: string) => {
|
||||
let found = false;
|
||||
while (!found) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await (new Promise((r) => requestAnimationFrame(r)));
|
||||
|
||||
found = this.navigationComponentIdStack.includes(componentId);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Waits until a passed screen is the top screen
|
||||
* Use this function only if you know what you are doing
|
||||
* this function will run until the screen is in the top
|
||||
* @param componentId string
|
||||
*/
|
||||
waitUntilScreenIsTop = async (componentId: string) => {
|
||||
let found = false;
|
||||
while (!found) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await (new Promise((r) => requestAnimationFrame(r)));
|
||||
|
||||
found = this.getNavigationTopComponentId() === componentId;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Waits until a screen has been removed as part of the stack.
|
||||
* Use this function only if you know what you are doing
|
||||
* this function will run until the screen disappears from the stack
|
||||
* and can easily run forever if the screen is never removed.
|
||||
* @param componentId string
|
||||
*/
|
||||
waitUntilScreensIsRemoved = async (componentId: string) => {
|
||||
let found = false;
|
||||
while (!found) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await (new Promise((r) => requestAnimationFrame(r)));
|
||||
|
||||
found = !this.navigationComponentIdStack.includes(componentId);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default new NavigationStore();
|
||||
@@ -8,6 +8,7 @@ import tinyColor from 'tinycolor2';
|
||||
import {Preferences} from '@constants';
|
||||
import {MODAL_SCREENS_WITHOUT_BACK} from '@constants/screens';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
import {appearanceControlledScreens, mergeNavigationOptions} from '@utils/navigation';
|
||||
|
||||
import type {Options} from 'react-native-navigation';
|
||||
@@ -113,7 +114,7 @@ export function setNavigatorStyles(componentId: string, theme: Theme, additional
|
||||
}
|
||||
|
||||
export function setNavigationStackStyles(theme: Theme) {
|
||||
EphemeralStore.allNavigationComponentIds.forEach((componentId) => {
|
||||
NavigationStore.allNavigationComponentIds.forEach((componentId) => {
|
||||
if (!appearanceControlledScreens.has(componentId)) {
|
||||
setNavigatorStyles(componentId, theme);
|
||||
}
|
||||
|
||||
18
index.ts
18
index.ts
@@ -16,7 +16,7 @@ import GlobalEventHandler from './app/managers/global_event_handler';
|
||||
import NetworkManager from './app/managers/network_manager';
|
||||
import WebsocketManager from './app/managers/websocket_manager';
|
||||
import {registerScreens} from './app/screens';
|
||||
import EphemeralStore from './app/store/ephemeral_store';
|
||||
import NavigationStore from './app/store/navigation_store';
|
||||
import setFontFamily from './app/utils/font_family';
|
||||
import './app/utils/emoji'; // Imported to ensure it is loaded when used
|
||||
|
||||
@@ -92,7 +92,7 @@ function screenWillAppear({componentId}: ComponentDidAppearEvent) {
|
||||
|
||||
function screenDidAppearListener({componentId, passProps, componentType}: ComponentDidAppearEvent) {
|
||||
if (!(passProps as any)?.overlay && componentType === 'Component') {
|
||||
EphemeralStore.addNavigationComponentId(componentId);
|
||||
NavigationStore.addNavigationComponentId(componentId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,25 +102,25 @@ function screenDidDisappearListener({componentId}: ComponentDidDisappearEvent) {
|
||||
DeviceEventEmitter.emit(Events.PAUSE_KEYBOARD_TRACKING_VIEW, false);
|
||||
}
|
||||
|
||||
if (EphemeralStore.getNavigationTopComponentId() === Screens.HOME) {
|
||||
if (NavigationStore.getNavigationTopComponentId() === Screens.HOME) {
|
||||
DeviceEventEmitter.emit(Events.TAB_BAR_VISIBLE, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function screenPoppedListener({componentId}: ScreenPoppedEvent) {
|
||||
EphemeralStore.removeNavigationComponentId(componentId);
|
||||
if (EphemeralStore.getNavigationTopComponentId() === Screens.HOME) {
|
||||
NavigationStore.removeNavigationComponentId(componentId);
|
||||
if (NavigationStore.getNavigationTopComponentId() === Screens.HOME) {
|
||||
DeviceEventEmitter.emit(Events.TAB_BAR_VISIBLE, true);
|
||||
}
|
||||
}
|
||||
|
||||
function modalDismissedListener({componentId}: ModalDismissedEvent) {
|
||||
const topScreen = EphemeralStore.getNavigationTopComponentId();
|
||||
const topModal = EphemeralStore.getNavigationTopModalId();
|
||||
const topScreen = NavigationStore.getNavigationTopComponentId();
|
||||
const topModal = NavigationStore.getNavigationTopModalId();
|
||||
const toRemove = topScreen === topModal ? topModal : componentId;
|
||||
EphemeralStore.removeNavigationModal(toRemove);
|
||||
if (EphemeralStore.getNavigationTopComponentId() === Screens.HOME) {
|
||||
NavigationStore.removeNavigationModal(toRemove);
|
||||
if (NavigationStore.getNavigationTopComponentId() === Screens.HOME) {
|
||||
DeviceEventEmitter.emit(Events.TAB_BAR_VISIBLE, true);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user