Show friends relative last online time

This commit is contained in:
Samuel Elliott 2022-08-16 18:38:06 +01:00
parent 9fa92fd6cc
commit 0d54381c6d
No known key found for this signature in database
GPG Key ID: 8420C7CDE43DC4D6
3 changed files with 61 additions and 4 deletions

View File

@ -8,7 +8,7 @@ import { getTitleIdFromEcUrl, hrduration } from '../../../util/misc.js';
import { Button } from '../components/index.js';
import { DEFAULT_ACCENT_COLOUR, TEXT_COLOUR_ACTIVE, TEXT_COLOUR_DARK, TEXT_COLOUR_LIGHT } from '../constants.js';
import ipc, { events } from '../ipc.js';
import { RequestState, Root, useAccentColour, useAsync, useColourScheme, useDiscordPresenceSource, useEventListener } from '../util.js';
import { RequestState, Root, useAccentColour, useAsync, useColourScheme, useDiscordPresenceSource, useEventListener, useTimeSince } from '../util.js';
export interface FriendProps {
user: string;
@ -86,6 +86,8 @@ export default function Friend(props: FriendProps) {
<Text style={[styles.friendCreatedAt, theme.text]}>Friends since {new Date(friend.friendCreatedAt * 1000).toLocaleString('en-GB')}</Text>
{friend.presence.updatedAt ? <Text style={[styles.presenceUpdatedAt, theme.text]}>Presence updated at {new Date(friend.presence.updatedAt * 1000).toLocaleString('en-GB')}</Text> : null}
{!(friend.presence.state === PresenceState.ONLINE || friend.presence.state === PresenceState.PLAYING) &&
friend.presence.logoutAt ? <Text style={[styles.presenceUpdatedAt, theme.text]}>Last online at {new Date(friend.presence.logoutAt * 1000).toLocaleString('en-GB')}</Text> : null}
<Text style={[styles.canSeeUserPresence, theme.text]}>This user {can_see_user_presence ? 'can' : 'can not'} see your presence.</Text>
</View>
@ -120,6 +122,7 @@ function FriendPresence(props: {
const theme = useColourScheme() === 'light' ? light : dark;
const logout = props.presence.logoutAt ? new Date(props.presence.logoutAt * 1000) : null;
const since_logout = useTimeSince(logout ?? new Date(0));
const game = 'name' in props.presence.game ? props.presence.game : null;
if (props.presence.state === PresenceState.ONLINE || props.presence.state === PresenceState.PLAYING) {
@ -128,7 +131,7 @@ function FriendPresence(props: {
return <View>
<Text style={[styles.presenceText, styles.presenceTextOffline, theme.text]}>Offline</Text>
{logout ? <Text style={[styles.presenceText, styles.presenceTextOffline, theme.text]}>Last seen {logout.toLocaleString('en-GB')}</Text> : null}
{logout ? <Text style={[styles.presenceText, styles.presenceTextOffline, theme.text]}>Last seen {since_logout}</Text> : null}
</View>;
}

View File

@ -1,7 +1,7 @@
import React, { useCallback } from 'react';
import { Image, ImageStyle, Platform, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import ipc from '../ipc.js';
import { useAccentColour, useColourScheme, User } from '../util.js';
import { useAccentColour, useColourScheme, User, useTimeSince } from '../util.js';
import { Friend, Presence, PresenceState } from '../../../api/coral-types.js';
import { TEXT_COLOUR_ACTIVE, TEXT_COLOUR_DARK, TEXT_COLOUR_LIGHT } from '../constants.js';
import Section, { HEADER_SIZE } from './section.js';
@ -95,11 +95,14 @@ function FriendPresence(props: {
}) {
const theme = useColourScheme() === 'light' ? light : dark;
const logout = props.presence.logoutAt ? new Date(props.presence.logoutAt * 1000) : null;
const since_logout = useTimeSince(logout ?? new Date(0), true);
if (props.presence.state === PresenceState.ONLINE || props.presence.state === PresenceState.PLAYING) {
return <Text style={[styles.presenceText, theme.text, styles.presenceTextOnline]}>Playing</Text>;
}
return <Text style={[styles.presenceText, styles.presenceTextOffline, theme.text]}>Offline</Text>;
return <Text style={[styles.presenceText, styles.presenceTextOffline, theme.text]}>{logout ? since_logout : 'Offline'}</Text>;
}
const styles = StyleSheet.create({

View File

@ -249,3 +249,54 @@ export function useActiveDiscordUser() {
return user;
}
export function useTimeSince(time: Date, short = false) {
const [now, setNow] = useState(Date.now());
const [since, update_in] = getTimeSince(time, now, short ? short_time_since_intervals : time_since_intervals);
const update_at = Date.now() + update_in;
useEffect(() => {
const timeout = setTimeout(() => setNow(Date.now()), Math.max(1000, update_at - Date.now()));
return () => clearTimeout(timeout);
}, [time, short, update_at]);
return since;
}
interface TimeSinceInterval {
interval: number;
max: number;
string: (count: number) => string;
}
const time_since_intervals: TimeSinceInterval[] = [
{interval: 1000, max: 10, string: () => 'just now'},
{interval: 1000, max: 60, string: c => c + ' second' + (c === 1 ? '' : 's') + ' ago'},
{interval: 60 * 1000, max: 60, string: c => c + ' minute' + (c === 1 ? '' : 's') + ' ago'},
{interval: 60 * 60 * 1000, max: 24, string: c => c + ' hour' + (c === 1 ? '' : 's') + ' ago'},
{interval: 24 * 60 * 60 * 1000, max: Infinity, string: c => c + ' day' + (c === 1 ? '' : 's') + ' ago'},
];
const short_time_since_intervals: TimeSinceInterval[] = [
{interval: 1000, max: 10, string: () => 'Just now'},
{interval: 1000, max: 60, string: c => c + ' sec' + (c === 1 ? '' : 's')},
{interval: 60 * 1000, max: 60, string: c => c + ' min' + (c === 1 ? '' : 's')},
{interval: 60 * 60 * 1000, max: 24, string: c => c + ' hr' + (c === 1 ? '' : 's')},
{interval: 24 * 60 * 60 * 1000, max: Infinity, string: c => c + ' day' + (c === 1 ? '' : 's')},
];
function getTimeSince(time: Date | number, now = Date.now(), intervals = time_since_intervals): [string, number] {
if (time instanceof Date) time = time.getTime();
const elapsed = Math.max(0, now - time);
const last = intervals[time_since_intervals.length - 1];
for (const i of intervals) {
if (elapsed < i.max * i.interval || last === i) {
const count = Math.floor(elapsed / i.interval);
return [i.string.call(null, count), i.interval - (elapsed - (count * i.interval))];
}
}
throw new Error('Invalid intervals');
}