diff --git a/package-lock.json b/package-lock.json index 13fd8cc..d7ad937 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,9 +21,10 @@ "moment": "^2.29.1", "nyc-dark": "^3.0.3", "pg": "^8.7.1", - "react": "^18.2.0", + "react": "^17.0.2", "react-contenteditable": "^3.3.6", - "recoil": "^0.7.5", + "react-dom": "^17.0.2", + "recoil": "^0.5.2", "sass": "^1.43.4", "sharp": "^0.31.1", "socket.io": "^4.3.1", @@ -48,7 +49,6 @@ "eslint-plugin-unused-imports": "^2.0.0", "jest": "^29.1.2", "prettier": "^2.5.1", - "react-dom": "^18.2.0", "ts-jest": "^29.0.3", "typescript": "^4.4.4" } @@ -6461,11 +6461,12 @@ } }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", "dependencies": { - "loose-envify": "^1.1.0" + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" }, "engines": { "node": ">=0.10.0" @@ -6484,16 +6485,16 @@ } }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "dev": true, + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" }, "peerDependencies": { - "react": "^18.2.0" + "react": "17.0.2" } }, "node_modules/react-is": { @@ -6542,9 +6543,9 @@ } }, "node_modules/recoil": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.5.tgz", - "integrity": "sha512-GVShsj5+M/2GULWBs5WBJGcsNis/d3YvDiaKjYh3mLKXftjtmk9kfaQ8jwjoIXySCwn8/RhgJ4Sshwgzj2UpFA==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.5.2.tgz", + "integrity": "sha512-Edibzpu3dbUMLy6QRg73WL8dvMl9Xqhp+kU+f2sJtXxsaXvAlxU/GcnDE8HXPkprXrhHF2e6SZozptNvjNF5fw==", "dependencies": { "hamt_plus": "1.0.2" }, @@ -6773,12 +6774,12 @@ } }, "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dev": true, + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", "dependencies": { - "loose-envify": "^1.1.0" + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" } }, "node_modules/semver": { @@ -12771,11 +12772,12 @@ } }, "react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", "requires": { - "loose-envify": "^1.1.0" + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" } }, "react-contenteditable": { @@ -12788,13 +12790,13 @@ } }, "react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "dev": true, + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", "requires": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" } }, "react-is": { @@ -12830,9 +12832,9 @@ } }, "recoil": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.5.tgz", - "integrity": "sha512-GVShsj5+M/2GULWBs5WBJGcsNis/d3YvDiaKjYh3mLKXftjtmk9kfaQ8jwjoIXySCwn8/RhgJ4Sshwgzj2UpFA==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.5.2.tgz", + "integrity": "sha512-Edibzpu3dbUMLy6QRg73WL8dvMl9Xqhp+kU+f2sJtXxsaXvAlxU/GcnDE8HXPkprXrhHF2e6SZozptNvjNF5fw==", "requires": { "hamt_plus": "1.0.2" } @@ -12980,12 +12982,12 @@ } }, "scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dev": true, + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", "requires": { - "loose-envify": "^1.1.0" + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" } }, "semver": { diff --git a/package.json b/package.json index bfda255..4166187 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,10 @@ "moment": "^2.29.1", "nyc-dark": "^3.0.3", "pg": "^8.7.1", - "react": "^18.2.0", + "react": "^17.0.2", "react-contenteditable": "^3.3.6", - "recoil": "^0.7.5", + "react-dom": "^17.0.2", + "recoil": "^0.5.2", "sass": "^1.43.4", "sharp": "^0.31.1", "socket.io": "^4.3.1", @@ -45,7 +46,6 @@ "eslint-plugin-unused-imports": "^2.0.0", "jest": "^29.1.2", "prettier": "^2.5.1", - "react-dom": "^18.2.0", "ts-jest": "^29.0.3", "typescript": "^4.4.4" } diff --git a/src/client/webapp/auto-verifier-with-args.ts b/src/client/webapp/auto-verifier-with-args.ts index 7f00ca1..c222a5d 100644 --- a/src/client/webapp/auto-verifier-with-args.ts +++ b/src/client/webapp/auto-verifier-with-args.ts @@ -26,14 +26,12 @@ export class AutoVerifierWithArg { private tokenizer: (query: K) => string, // must be one-to-one mapping private primaryFunc: (query: K) => Promise, private trustedFunc: (query: K) => Promise, - private ensureTrustedFuncReady: () => Promise, private verifyFunc: (query: K, primaryResult: T | null, trustedResult: T | null) => Promise, ) {} static createStandardPartialMessageListAutoVerifier & { id: string }>( primaryFunc: (query: PartialMessageListQuery) => Promise, trustedFunc: (query: PartialMessageListQuery) => Promise, - ensureTrustedFuncReady: () => Promise, changesFunc: ( query: PartialMessageListQuery, changesType: AutoVerifierChangesType, @@ -44,7 +42,6 @@ export class AutoVerifierWithArg { query => `ch#${query.channelId} mo#${query.messageOrderId}->${query.number}`, query => primaryFunc(query), query => trustedFunc(query), - ensureTrustedFuncReady, async (query: PartialMessageListQuery, primaryResult: T[] | null, trustedResult: T[] | null) => { // lOG.debug('messages verify: ', { // query, @@ -66,7 +63,6 @@ export class AutoVerifierWithArg { static createStandardIDQueriedSingleAutoVerifier & { id: string }>( primaryFunc: (query: IDQuery) => Promise, trustedFunc: (query: IDQuery) => Promise, - ensureTrustedFuncReady: () => Promise, changesFunc: ( query: IDQuery, changesType: AutoVerifierChangesType, @@ -78,7 +74,6 @@ export class AutoVerifierWithArg { query => `id#${query.id}`, query => primaryFunc(query), query => trustedFunc(query), - ensureTrustedFuncReady, async (query: IDQuery, primaryResult: T | null, trustedResult: T | null) => { const changesType = AutoVerifier.getSingleChangesType(primaryResult, trustedResult); return await changesFunc(query, changesType, primaryResult, trustedResult); @@ -120,7 +115,6 @@ export class AutoVerifierWithArg { autoVerifier = new AutoVerifier( async () => await this.primaryFunc(query), async () => await this.trustedFunc(query), - this.ensureTrustedFuncReady, async (primaryResult: T | null, trustedResult: T | null) => await this.verifyFunc(query, primaryResult, trustedResult), ); diff --git a/src/client/webapp/elements/lists/components/guild-list-element.tsx b/src/client/webapp/elements/lists/components/guild-list-element.tsx index 0c422c0..558f217 100644 --- a/src/client/webapp/elements/lists/components/guild-list-element.tsx +++ b/src/client/webapp/elements/lists/components/guild-list-element.tsx @@ -1,3 +1,8 @@ +import * as electronRemote from '@electron/remote'; +const electronConsole = electronRemote.getGlobal('console') as Console; +import Logger from '../../../../../logger/logger'; +const LOG = Logger.create(__filename, electronConsole); + import React, { FC, useCallback, useMemo, useRef } from 'react'; import { useRecoilState, useRecoilValue } from 'recoil'; import CombinedGuild from '../../../guild-combined'; @@ -18,6 +23,10 @@ const GuildListElement: FC = (props: GuildListElementProp const rootRef = useRef(null); + useMemo(() => { + LOG.debug(`list element guild id: ${guild.id}`) + }, [ guild.id ]); + const guildsManager = useRecoilValue(guildsManagerState); const [currGuildId, setCurrGuildId] = useRecoilState(currGuildIdState); const guildMeta = useRecoilValue(guildMetaState(guild.id)); diff --git a/src/client/webapp/elements/require/atoms-funcs.ts b/src/client/webapp/elements/require/atoms-funcs.ts index 067ceef..02a71cf 100644 --- a/src/client/webapp/elements/require/atoms-funcs.ts +++ b/src/client/webapp/elements/require/atoms-funcs.ts @@ -29,6 +29,7 @@ import { } from './loadables'; import { guildState } from './atoms'; import { Changes } from '../../data-types'; +import { randomUUID } from 'crypto'; // general typescript type that infers the arguments of a function export type Arguments = T extends (...args: infer A) => unknown ? A : never; @@ -46,15 +47,21 @@ export function createFetchValueFunc( ): FetchValueFunc { const { node, setSelf, getPromise } = atomEffectParam; const fetchValueFunc = async () => { + const fvfId = randomUUID().slice(0, 4); + LOG.debug(`${fvfId} awaiting getPromise(guildState(${guildId}))`); const guild = await getPromise(guildState(guildId)); + LOG.debug(`${fvfId} got guild from gm for g#${guildId}: ${guild}`); if (guild === null) return; // NOTE: This would only happen if this atom is created before its corresponding guild exists in the guildsManager const selfState = await getPromise(node); if (isPended(selfState)) return; // don't send another request if we're already loading + LOG.debug(`${fvfId} setting to pended`); setSelf(DEF_PENDED_VALUE); try { + LOG.debug(`${fvfId} fetchFunc before`); const value = await fetchFunc(guild); + LOG.debug(`${fvfId} fetchFunc after`); setSelf(createLoadedValue(value, fetchValueFunc)); } catch (e: unknown) { setSelf(createFailedValue(e, fetchValueFunc)); @@ -349,13 +356,16 @@ export function guildDataSubscriptionLoadableSingleEffect< const { trigger } = atomEffectParam; if (skipFunc && skipFunc()) return; // don't run if this atom should be skipped for some reason (e.g. null resourceId) + LOG.debug(`single effect for guild #${guildId}`); const fetchValueFunc = createFetchValueFunc(atomEffectParam, guildId, fetchFunc); // fetch initial value on first get if (trigger === 'get') { (async () => { try { + LOG.debug('awaiting fetchValueFunc Single'); await fetchValueFunc(); + LOG.debug('done fetchValueFunc Single'); } catch (e: unknown) { LOG.error('error fetching initial value', e); } @@ -386,13 +396,16 @@ export function guildDataSubscriptionLoadableMultipleEffect< const effect: AtomEffect> = atomEffectParam => { const { trigger } = atomEffectParam; + /* LOG.debug(`multiple effect for guild #${guildId}`); */ const fetchValueFunc = createFetchValueFunc(atomEffectParam, guildId, fetchFunc); // fetch initial value on first get if (trigger === 'get') { (async () => { try { + /* LOG.debug('awaiting fetchValueFunc Multiple'); */ await fetchValueFunc(); + /* LOG.debug('done fetchValueFunc Multiple'); */ } catch (e: unknown) { LOG.error('error fetching initial value', e); } diff --git a/src/client/webapp/elements/require/atoms.ts b/src/client/webapp/elements/require/atoms.ts index 6d4a2b7..7e30626 100644 --- a/src/client/webapp/elements/require/atoms.ts +++ b/src/client/webapp/elements/require/atoms.ts @@ -56,6 +56,7 @@ import { multipleScrollingGuildSubscriptionEffect, useRecoilValueLoadableOrElse, } from './atoms-funcs'; +import { randomUUID } from 'crypto'; export const overlayState = atom({ key: 'overlayState', @@ -86,7 +87,10 @@ export const guildMetaState = atomFamily, number>({ effects_UNSTABLE: (guildId: number) => [ guildDataSubscriptionLoadableSingleEffect( guildId, - async (guild: CombinedGuild) => await guild.fetchMetadata(), + async (guild: CombinedGuild) => { + LOG.debug('fetching guild metadata', { guild }); + return await guild.fetchMetadata(); + }, { updatedEvent: { name: 'update-metadata', @@ -378,9 +382,13 @@ export const guildState = selectorFamily({ get: (guildId: number) => ({ get }) => { + const id = randomUUID().slice(0, 4); + LOG.debug(`${id} - use guildState g#${guildId}`) const guildsManager = get(guildsManagerState); + LOG.debug(`${id} - gm - ${guildsManager ? guildsManager.guilds.length + ' guilds' : guildsManager} - [${guildsManager?.guilds.map(guild => guild.id) ?? ''}]`); if (guildsManager === null) return null; const guild = guildsManager.guilds.find(guild => guild.id === guildId) ?? null; + LOG.debug(`${id} - gm guild member: m#${guild?.memberId}`); return guild; }, dangerouslyAllowMutability: true, @@ -394,6 +402,7 @@ export const currGuildIdState = atom({ function createCurrentGuildStateGetter(subSelectorFamily: (guildId: number) => RecoilValueReadOnly) { return ({ get }: { get: GetRecoilValue }) => { const currGuildId = get(currGuildIdState); + LOG.debug(`current guild id is ${currGuildId} - guild state getter`); if (currGuildId === null) return null; const value = get(subSelectorFamily(currGuildId)); @@ -540,6 +549,7 @@ export function useRecoilValueSoftImgSrc(recoilValue: RecoilValue): stri // initialize with a guildsManager export function useInitRecoil(guildsManager: GuildsManager) { + LOG.debug(`recoil init, gm has ${guildsManager.guilds.length} guilds`); const setGuildsManager = useSetRecoilState(guildsManagerState); const setGuilds = useSetRecoilState(allGuildsState); const [currGuildId, setCurrGuildId] = useRecoilState(currGuildIdState); diff --git a/src/client/webapp/elements/sections/guild.tsx b/src/client/webapp/elements/sections/guild.tsx index 521d9ff..4e0f265 100644 --- a/src/client/webapp/elements/sections/guild.tsx +++ b/src/client/webapp/elements/sections/guild.tsx @@ -21,15 +21,19 @@ const GuildElement: FC = () => { // TODO: Handle fetch errors in message list // TODO: React set hasMessagesAbove and hasMessagesBelow when re-verified? + LOG.debug('grabbing recoil values for current guild'); const guild = useRecoilValue(currGuildState); const selfMember = useRecoilValue(currGuildSelfMemberState); const channels = useRecoilValue(currGuildChannelsState); const activeChannel = useRecoilValue(currGuildActiveChannelState); const setActiveChannelId = useSetRecoilState(guildActiveChannelIdState(guild?.id ?? -1)); - // useEffect(() => { - // lOG.debug('guild changed', { guildId: guild?.id ?? '' }); - // }, [ guild ]); + useEffect(() => { + LOG.debug('guild changed', { guildId: guild?.id ?? '' }); + }, [ guild ]); + useEffect(() => { + LOG.debug('self member changed', { selfMember }); + }, [ selfMember ]); // useEffect(() => { // lOG.debug('active channel changed', { activeChannel }); // }, [ activeChannel ]) diff --git a/src/client/webapp/fetchable-pair-verifier.ts b/src/client/webapp/fetchable-pair-verifier.ts index 233ac36..0501471 100644 --- a/src/client/webapp/fetchable-pair-verifier.ts +++ b/src/client/webapp/fetchable-pair-verifier.ts @@ -38,21 +38,18 @@ export default class PairVerifierFetchable extends EventEmitter im this.fetchMetadataVerifier = AutoVerifier.createStandardSingleAutoVerifier( async () => await this.primary.fetchMetadata(), async () => await this.trusted.fetchMetadata(), - async () => await this.trusted.ensureVerified(), this.handleMetadataConflict.bind(this), ); this.fetchMembersVerifier = AutoVerifier.createStandardListAutoVerifier( async () => await this.primary.fetchMembers(), async () => await this.trusted.fetchMembers(), - async () => await this.trusted.ensureVerified(), this.handleMembersConflict.bind(this), ); this.fetchChannelsVerifier = AutoVerifier.createStandardListAutoVerifier( async () => await this.primary.fetchChannels(), async () => await this.trusted.fetchChannels(), - async () => await this.trusted.ensureVerified(), this.handleChannelsConflict.bind(this), ); @@ -61,14 +58,12 @@ export default class PairVerifierFetchable extends EventEmitter im // async () => { LOG.debug('fetching primary tokens for ' + this.trusted.constructor.name); return await this.trusted.fetchTokens() }, async () => await this.primary.fetchTokens(), async () => await this.trusted.fetchTokens(), - async () => await this.trusted.ensureVerified(), this.handleTokensConflict.bind(this), ); this.fetchResourceVerifier = AutoVerifierWithArg.createStandardIDQueriedSingleAutoVerifier( async (query: IDQuery) => await this.primary.fetchResource(query.id), async (query: IDQuery) => await this.trusted.fetchResource(query.id), - async () => await this.trusted.ensureVerified(), this.handleResourceConflict.bind(this), ); @@ -77,7 +72,6 @@ export default class PairVerifierFetchable extends EventEmitter im await this.primary.fetchMessagesRecent(query.channelId, query.number), async (query: PartialMessageListQuery) => await this.trusted.fetchMessagesRecent(query.channelId, query.number), - async () => await this.trusted.ensureVerified(), this.handleMessagesConflict.bind(this), ); @@ -86,7 +80,6 @@ export default class PairVerifierFetchable extends EventEmitter im await this.primary.fetchMessagesBefore(query.channelId, query.messageOrderId as string, query.number), async (query: PartialMessageListQuery) => await this.trusted.fetchMessagesBefore(query.channelId, query.messageOrderId as string, query.number), - async () => await this.trusted.ensureVerified(), this.handleMessagesConflict.bind(this), ); @@ -95,7 +88,6 @@ export default class PairVerifierFetchable extends EventEmitter im await this.primary.fetchMessagesAfter(query.channelId, query.messageOrderId as string, query.number), async (query: PartialMessageListQuery) => await this.trusted.fetchMessagesAfter(query.channelId, query.messageOrderId as string, query.number), - async () => await this.trusted.ensureVerified(), this.handleMessagesConflict.bind(this), ); } diff --git a/src/client/webapp/guild-combined.ts b/src/client/webapp/guild-combined.ts index 6eca2ea..33944bb 100644 --- a/src/client/webapp/guild-combined.ts +++ b/src/client/webapp/guild-combined.ts @@ -355,18 +355,22 @@ export default class CombinedGuild // fetched through the triple-cache system (RAM -> Disk -> Server) async fetchMetadata(): Promise { // xUtil.failSometimes(0.05); // for testing + LOG.debug(`g#${this.id}: fetch metadata`); return await this.fetchable.fetchMetadata(); } async fetchMembers(): Promise { // xUtil.failSometimes(0.05); // for testing + LOG.debug(`g#${this.id}: fetch members`); return await this.fetchable.fetchMembers(); } async fetchChannels(): Promise { // xUtil.failSometimes(0.05); // for testing + LOG.debug(`g#${this.id}: fetch channels`); return await this.fetchable.fetchChannels(); } async fetchMessagesRecent(channelId: string, number: number): Promise { // xUtil.failSometimes(0.05); // for testing + LOG.debug(`g#${this.id}: fetch recent messages`); const members = await this.grabRAMMembersMap(); const channels = await this.grabRAMChannelsMap(); const messages = await this.fetchable.fetchMessagesRecent(channelId, number); @@ -377,6 +381,7 @@ export default class CombinedGuild } async fetchMessagesBefore(channelId: string, messageOrderId: string, number: number): Promise { // xUtil.failSometimes(0.05); // for testing + LOG.debug(`g#${this.id}: fetch messages before`); const members = await this.grabRAMMembersMap(); const channels = await this.grabRAMChannelsMap(); const messages = await this.fetchable.fetchMessagesBefore(channelId, messageOrderId, number); @@ -387,6 +392,7 @@ export default class CombinedGuild } async fetchMessagesAfter(channelId: string, messageOrderId: string, number: number): Promise { Util.failSometimes(0.75); // for testing + LOG.debug(`g#${this.id}: fetch messages after`); const members = await this.grabRAMMembersMap(); const channels = await this.grabRAMChannelsMap(); const messages = await this.fetchable.fetchMessagesAfter(channelId, messageOrderId, number); @@ -397,10 +403,12 @@ export default class CombinedGuild } async fetchResource(resourceId: string): Promise { // xUtil.failSometimes(0.05); // for testing + LOG.debug(`g#${this.id}: fetch resource`); return await this.fetchable.fetchResource(resourceId); } async fetchTokens(): Promise { // xUtil.failSometimes(0.05); // for testing + LOG.debug(`g#${this.id}: fetch tokens`); const members = await this.grabRAMMembersMap(); const tokens = await this.fetchable.fetchTokens(); for (const token of tokens) {