diff --git a/src/api/coral.ts b/src/api/coral.ts index 4968dda..78ba195 100644 --- a/src/api/coral.ts +++ b/src/api/coral.ts @@ -61,6 +61,8 @@ export interface CoralApiInterface { getUser(id: number): Promise>; getUserByFriendCode(friend_code: string, hash?: string): Promise>; getCurrentUser(): Promise>; + getReceivedFriendRequests(): Promise>; + getSentFriendRequests(): Promise>; getFriendCodeUrl(): Promise>; getCurrentUserPermissions(): Promise>; getWebServiceToken(id: number): Promise>; diff --git a/src/app/browser/add-friend/index.tsx b/src/app/browser/add-friend/index.tsx index 401673a..e34061e 100644 --- a/src/app/browser/add-friend/index.tsx +++ b/src/app/browser/add-friend/index.tsx @@ -36,6 +36,11 @@ export default function AddFriendWindow(props: AddFriendProps) { const [user] = useAsync(useCallback(() => token ? ipc.getSavedCoralToken(token) : Promise.resolve(null), [ipc, token])); + const [fr_received, fr_received_error, fr_received_state] = useAsync(useCallback(() => token ? + ipc.getNsoReceivedFriendRequests(token) : Promise.resolve(null), [ipc, token])); + const [fr_sent, fr_sent_error, fr_sent_state] = useAsync(useCallback(() => token ? + ipc.getNsoSentFriendRequests(token) : Promise.resolve(null), [ipc, token])); + const [friendcode, setFriendCode] = useState(props.friendcode ?? ''); const is_valid_friendcode = FRIEND_CODE.test(friendcode); const show_friendcode_field = !props.friendcode || !FRIEND_CODE.test(props.friendcode); diff --git a/src/app/browser/main/friends.tsx b/src/app/browser/main/friends.tsx index 0648d43..ecca42a 100644 --- a/src/app/browser/main/friends.tsx +++ b/src/app/browser/main/friends.tsx @@ -3,7 +3,7 @@ import { Image, ImageStyle, Platform, ScrollView, StyleSheet, Text, TouchableOpa import { Trans, useTranslation } from 'react-i18next'; import ipc from '../ipc.js'; import { useAccentColour, useColourScheme, User, useTimeSince } from '../util.js'; -import { Friend, Presence, PresenceState } from '../../../api/coral-types.js'; +import { Friend_4, 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'; import AddOutline from '../components/icons/add-outline.js'; @@ -11,7 +11,7 @@ import { FriendCode } from '../components/index.js'; export default function Friends(props: { user: User; - friends: Friend[]; + friends: Friend_4[]; loading?: boolean; error?: Error; }) { @@ -52,7 +52,7 @@ export default function Friends(props: { } function Friend(props: { - friend: Friend; + friend: Friend_4; user?: User; }) { const theme = useColourScheme() === 'light' ? light : dark; diff --git a/src/app/main/ipc.ts b/src/app/main/ipc.ts index 8342966..9236b06 100644 --- a/src/app/main/ipc.ts +++ b/src/app/main/ipc.ts @@ -90,6 +90,8 @@ export function setupIpc(appinstance: App, ipcMain: IpcMain) { null))); handle('coral:activeevent', (e, token: string) => store.users.get(token).then(u => u.getActiveEvent())); handle('coral:friendcodeurl', (e, token: string) => store.users.get(token).then(u => u.nso.getFriendCodeUrl())); + handle('coral:friendrequests:received', (e, token: string) => store.users.get(token).then(u => u.nso.getReceivedFriendRequests())); + handle('coral:friendrequests:sent', (e, token: string) => store.users.get(token).then(u => u.nso.getSentFriendRequests())); handle('coral:friendcode', (e, token: string, friendcode: string, hash?: string) => store.users.get(token).then(u => u.nso.getUserByFriendCode(friendcode, hash))); handle('coral:addfriend', (e, token: string, nsaid: string) => store.users.get(token).then(u => u.addFriend(nsaid))); diff --git a/src/app/preload/index.ts b/src/app/preload/index.ts index 0895cc4..0636c61 100644 --- a/src/app/preload/index.ts +++ b/src/app/preload/index.ts @@ -7,7 +7,7 @@ import type { SavedToken } from '../../common/auth/coral.js'; import type { SavedMoonToken } from '../../common/auth/moon.js'; import type { UpdateCacheData } from '../../common/update.js'; import type { StatusUpdate } from '../../common/status.js'; -import type { Announcements, CoralSuccessResponse, CurrentUser, Friend, FriendCodeUrl, FriendCodeUser, GetActiveEventResult, WebService, WebServices } from '../../api/coral-types.js'; +import type { Announcements, Announcements_4, CoralSuccessResponse, CurrentUser, Friend, Friend_4, FriendCodeUrl, FriendCodeUser, GetActiveEventResult, ReceivedFriendRequests, SentFriendRequests, WebService, WebServices, WebServices_4 } from '../../api/coral-types.js'; import type { DiscordPresence } from '../../discord/types.js'; import type { NintendoAccountUser } from '../../api/na.js'; import type { DiscordSetupProps } from '../browser/discord/index.js'; @@ -61,12 +61,14 @@ const ipc = { getNintendoAccountCoralToken: (id: string) => inv('coral:gettoken', id), getSavedCoralToken: (token: string) => inv('coral:getcachedtoken', token), - getCoralAnnouncements: (token: string) => inv('coral:announcements', token), - getNsoFriends: (token: string) => inv('coral:friends', token), - getWebServices: (token: string) => inv('coral:webservices', token), + getCoralAnnouncements: (token: string) => inv('coral:announcements', token), + getNsoFriends: (token: string) => inv('coral:friends', token), + getWebServices: (token: string) => inv('coral:webservices', token), openWebService: (webservice: WebService, token: string, qs?: string) => inv('coral:openwebservice', webservice, token, qs), getCoralActiveEvent: (token: string) => inv('coral:activeevent', token), getNsoFriendCodeUrl: (token: string) => inv('coral:friendcodeurl', token), + getNsoReceivedFriendRequests: (token: string) => inv('coral:friendrequests:received', token), + getNsoSentFriendRequests: (token: string) => inv('coral:friendrequests:sent', token), getNsoUserByFriendCode: (token: string, friendcode: string, hash?: string) => inv('coral:friendcode', token, friendcode, hash), addNsoFriend: (token: string, nsa_id: string) => inv<{result: CoralSuccessResponse<{}>; friend: Friend | null}>('coral:addfriend', token, nsa_id), diff --git a/src/common/users.ts b/src/common/users.ts index ad3455a..150c5bc 100644 --- a/src/common/users.ts +++ b/src/common/users.ts @@ -4,7 +4,7 @@ import { Response } from 'undici'; import createDebug from '../util/debug.js'; import CoralApi, { CoralApiInterface, NintendoAccountUserCoral, Result } from '../api/coral.js'; import ZncProxyApi from '../api/znc-proxy.js'; -import { Friend, GetActiveEventResult, CoralSuccessResponse, WebService, CoralError, Announcements_4, Friends_4, WebServices_4, ListMedia, ListChat, WebService_4, CurrentUser } from '../api/coral-types.js'; +import { Friend, GetActiveEventResult, CoralSuccessResponse, WebService, CoralError, Announcements_4, Friends_4, WebServices_4, ListMedia, ListChat, WebService_4, CurrentUser, ReceivedFriendRequests, SentFriendRequests } from '../api/coral-types.js'; import { getToken, SavedToken } from './auth/coral.js'; import type { Store } from '../app/main/index.js'; @@ -119,6 +119,9 @@ export class CoralUser implements CoralU promise = new Map>(); delay_retry_after_error_until: number | null = null; + fr_received: CoralSuccessResponse | null = null; + fr_sent: CoralSuccessResponse | null = null; + updated = { announcements: Date.now(), friends: Date.now(), @@ -127,6 +130,9 @@ export class CoralUser implements CoralU media: Date.now(), active_event: Date.now(), user: Date.now(), + + fr_received: null as number | null, + fr_sent: null as number | null, }; delay_retry_after_error = 5 * 1000; // 5 seconds @@ -147,7 +153,7 @@ export class CoralUser implements CoralU ) {} private async update(key: keyof CoralUser['updated'], callback: () => Promise, ttl: number) { - if ((this.updated[key] + ttl) < Date.now()) { + if (!this.updated[key] || (this.updated[key] + ttl) < Date.now()) { const promise = this.promise.get(key) ?? Promise.resolve().then(() => { const delay_retry = (this.delay_retry_after_error_until ?? 0) - Date.now(); @@ -264,6 +270,28 @@ export class CoralUser implements CoralU return this.user.result; } + async getReceivedFriendRequests() { + await this.update('fr_received', async () => { + // Always requested together when refreshing add friend page + this.getSentFriendRequests(); + + this.fr_received = await this.nso.getReceivedFriendRequests(); + }, this.update_interval); + + return this.fr_received!.result.friendRequests; + } + + async getSentFriendRequests() { + await this.update('fr_sent', async () => { + // Always requested together when refreshing add friend page + this.getReceivedFriendRequests(); + + this.fr_sent = await this.nso.getSentFriendRequests(); + }, this.update_interval); + + return this.fr_sent!.result.friendRequests; + } + async addFriend(nsa_id: string) { if (!(this.nso instanceof CoralApi)) { throw new Error('Cannot send friend requests using Coral API proxy');