Files
mattermost-mobile/app/screens/home/channel_list/servers/index.tsx
Elias Nahum 75ed884e65 Add column last_fetched_at to MyChannel & Thread tables and the migration (#6433)
* Add column last_fetched_at to MyChannel & Thread tables and the migration

* Fix schema tests

* Handle lastFetchAt, retrieve threads on init and properly observe thread unreads (#6436)

* [Gekidou] Set lastFetchAt when fetching posts for a channel (#6437)

* Set lastFetchAt when fetching posts for a channel

* When resetting _preparedState set always to null

* Revert changes in WS

* Handle and set lastFetchedAt for MyChannel in iOS push notification

* feedback review

* iOS fallback to last post createAt if no lastFetchAt set

* Handle lastFetchAt on Android push notifications

* create storePostsForChannel local action

* Fix iOS fallback to last post create_at

Co-authored-by: Daniel Espino García <larkox@gmail.com>
2022-06-29 13:28:50 -04:00

173 lines
5.7 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react';
import {IntlShape, useIntl} from 'react-intl';
import {StyleSheet} from 'react-native';
import ServerIcon from '@components/server_icon';
import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import {subscribeAllServers} from '@database/subscription/servers';
import {subscribeUnreadAndMentionsByServer, UnreadObserverArgs} from '@database/subscription/unreads';
import {useIsTablet} from '@hooks/device';
import {bottomSheet} from '@screens/navigation';
import ServerList from './servers_list';
import type ServersModel from '@typings/database/models/app/servers';
import type {UnreadMessages, UnreadSubscription} from '@typings/database/subscriptions';
const subscriptions: Map<string, UnreadSubscription> = new Map();
const styles = StyleSheet.create({
icon: {
alignItems: 'center',
justifyContent: 'center',
position: 'absolute',
zIndex: 10,
top: 10,
left: 16,
width: 40,
height: 40,
},
});
const sortServers = (servers: ServersModel[], intl: IntlShape) => {
function serverName(s: ServersModel) {
if (s.displayName === s.url) {
return intl.formatMessage({id: 'servers.default', defaultMessage: 'Default Server'});
}
return s.displayName;
}
return servers.sort((a, b) => {
return serverName(a).localeCompare(serverName(b));
});
};
export type ServersRef = {
openServers: () => void;
}
const Servers = React.forwardRef<ServersRef>((props, ref) => {
const intl = useIntl();
const [total, setTotal] = useState<UnreadMessages>({mentions: 0, unread: false});
const registeredServers = useRef<ServersModel[]|undefined>();
const currentServerUrl = useServerUrl();
const isTablet = useIsTablet();
const theme = useTheme();
const updateTotal = () => {
let unread = false;
let mentions = 0;
subscriptions.forEach((value) => {
unread = unread || value.unread;
mentions += value.mentions;
});
setTotal({mentions, unread});
};
const unreadsSubscription = (serverUrl: string, {myChannels, settings, threadMentionCount, threadUnreads}: UnreadObserverArgs) => {
const unreads = subscriptions.get(serverUrl);
if (unreads) {
let mentions = 0;
let unread = Boolean(threadUnreads);
for (const myChannel of myChannels) {
const isMuted = settings?.[myChannel.id]?.mark_unread === 'mention';
mentions += myChannel.mentionsCount;
unread = unread || (myChannel.isUnread && !isMuted);
}
unreads.mentions = mentions + threadMentionCount;
unreads.unread = unread;
subscriptions.set(serverUrl, unreads);
updateTotal();
}
};
const serversObserver = async (servers: ServersModel[]) => {
registeredServers.current = sortServers(servers, intl);
// unsubscribe mentions from servers that were removed
const allUrls = new Set(servers.map((s) => s.url));
const subscriptionsToRemove = [...subscriptions].filter(([key]) => !allUrls.has(key));
for (const [key, map] of subscriptionsToRemove) {
map.subscription?.unsubscribe();
subscriptions.delete(key);
updateTotal();
}
for (const server of servers) {
const {lastActiveAt, url} = server;
if (lastActiveAt && (url !== currentServerUrl) && !subscriptions.has(url)) {
const unreads: UnreadSubscription = {
mentions: 0,
unread: false,
};
subscriptions.set(url, unreads);
unreads.subscription = subscribeUnreadAndMentionsByServer(url, unreadsSubscription);
} else if ((!lastActiveAt || (url === currentServerUrl)) && subscriptions.has(url)) {
subscriptions.get(url)?.subscription?.unsubscribe();
subscriptions.delete(url);
updateTotal();
}
}
};
const onPress = useCallback(() => {
if (registeredServers.current?.length) {
const renderContent = () => {
return (
<ServerList servers={registeredServers.current!}/>
);
};
const snapPoints = ['50%', 10];
if (registeredServers.current.length > 3) {
snapPoints[0] = '90%';
}
const closeButtonId = 'close-your-servers';
bottomSheet({
closeButtonId,
renderContent,
snapPoints,
theme,
title: intl.formatMessage({id: 'your.servers', defaultMessage: 'Your servers'}),
});
}
}, [isTablet, theme]);
useImperativeHandle(ref, () => ({
openServers: onPress,
}), [onPress]);
useEffect(() => {
const subscription = subscribeAllServers(serversObserver);
return () => {
subscription?.unsubscribe();
subscriptions.forEach((unreads) => {
unreads.subscription?.unsubscribe();
});
subscriptions.clear();
};
}, []);
return (
<ServerIcon
hasUnreads={total.unread}
mentionCount={total.mentions}
onPress={onPress}
style={styles.icon}
testID={'channel_list.servers.server_icon'}
/>
);
});
Servers.displayName = 'Servers';
export default Servers;