add a touch more documentation in the code

This commit is contained in:
Michael Peters 2022-01-25 19:03:47 -06:00
parent 9ca2a22881
commit a0f25158d3
19 changed files with 162 additions and 82 deletions

View File

@ -1,7 +1,7 @@
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 * as electronRemote from '@electron/remote';
//const electronConsole = electronRemote.getGlobal('console') as Console;
//import Logger from '../../logger/logger';
//const LOG = Logger.create(__filename, electronConsole);
import { AutoVerifier, AutoVerifierChangesType } from "./auto-verifier";
import { Changes, WithEquals } from "./data-types";

View File

@ -38,6 +38,7 @@ export class AutoVerifier<T> {
private verifyFunc: (primaryResult: T | null, trustedResult: T | null) => Promise<boolean>
) {}
/** Returns the changes that must be made to primaryResult given trustedResult */
static getChanges<T extends WithEquals<T> & { id: string }>(primaryResult: T[] | null, trustedResult: T[] | null): Changes<T> {
const changes: Changes<T> = { added: [], updated: [], deleted: [] };

View File

@ -1,9 +1,4 @@
import * as electronRemote from '@electron/remote';
const electronConsole = electronRemote.getGlobal('console') as Console;
import Logger from '../../logger/logger';
const LOG = Logger.create(__filename, electronConsole);
// lets you wait until it is uncorked by someone before running
// Runs the promise unless it is already running
export default class DedupAwaiter<T> {
private promise: Promise<T> | null = null;

View File

@ -1,13 +1,6 @@
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 from 'react';
export interface HTMLElementWithRemoveSelf extends HTMLElement {
removeSelf: (() => void);
}
// This file contains JSX elements that can be used in place directly of storing the elements on the disk.
export default class BaseElements {
// Scraped directly from discord (#)

View File

@ -12,6 +12,8 @@ import * as uuid from 'uuid';
import Util from '../../util';
import CombinedGuild from '../../guild-combined';
// This class contains general element management functions
export interface IAlignment {
left?: string;
centerX?: string;
@ -52,6 +54,7 @@ export default class ElementsUtil {
setState(old => !start);
}
/** Gets a string compatible with an <img> src attribute */
static async getImageBufferSrc(buffer: Buffer): Promise<string> {
const result = await FileType.fromBuffer(buffer);
switch (result && result.mime) {
@ -68,6 +71,7 @@ export default class ElementsUtil {
}
}
/** Gets a string compatible with an <img> src attribute and uses a loading svg to start and an error png if it fails */
static async getImageSrcFromBufferFailSoftly(buff: Buffer | null): Promise<string> {
if (buff === null) {
return './img/loading.svg';
@ -108,6 +112,7 @@ export default class ElementsUtil {
}
// creates <span class="bold"/"italic"/"bold italic"/"underline"> spans to format the text (all in q.js element markup that gets converted to jsx [aka hypergaming])
/** Creates a JSX element from a text string like "**hello** *world* _my_ ***dudes***" */
static parseMessageText(text: string): JSX.Element {
const obj: SimpleQElement = { tag: 'span', content: [], class: null };
const stack: SimpleQElement[] = [ obj ];
@ -191,6 +196,7 @@ export default class ElementsUtil {
// means align the center of the element to the top of the relative to
// and align the left of the element to the right of the relative to plus 20 pixels
// relativeTo can be an { x, y } or HTMLElement
/** Aligns an element relative to another element or a position in the window */
static alignContextElement(
element: HTMLElement,
relativeTo: HTMLElement | { x: number, y: number },

View File

@ -16,6 +16,7 @@ import Globals from '../../globals';
import ElementsUtil from './elements-util';
// Abuse closures to get state in case it changed after an await call
// TODO: This function may be useless...
function getStateAfterAwait<T>(setState: Dispatch<SetStateAction<T>>): T {
let x: unknown;
setState(state => {
@ -25,22 +26,7 @@ function getStateAfterAwait<T>(setState: Dispatch<SetStateAction<T>>): T {
return x as T;
}
export type SingleSubscriptionEvents = {
'fetch': () => void;
'updated': () => void;
'conflict': () => void;
'fetch-error': () => void;
}
export type MultipleSubscriptionEvents<T> = {
'fetch': () => void;
'fetch-error': () => void;
'new': (newValues: T[]) => void;
'updated': (updatedValues: T[]) => void;
'removed': (removedValues: T[]) => void;
'conflict': (changes: Changes<T>) => void;
}
// Parameters used by base guildSubscription to fetch the initial value
interface EffectParams<T> {
guild: CombinedGuild;
// TODO: I changed this from value: T | null to just value: T. I think
@ -52,13 +38,16 @@ interface EffectParams<T> {
unbindEventsFunc: () => void;
}
// General typescript type that infers the arguments of a function
type Arguments<T> = T extends (...args: infer A) => unknown ? A : never;
// The result of a general subscription. Includes the value and associated guild
export interface SubscriptionResult<T> {
value: T;
guild: CombinedGuild;
}
// The result of a scrolling subscription. Includes the state of each end of the list, the list, and the associated guild
export interface ScrollingSubscriptionResult<T> {
value: {
ends: { hasMoreAbove: boolean, hasMoreBelow: boolean };
@ -67,10 +56,12 @@ export interface ScrollingSubscriptionResult<T> {
guild: CombinedGuild;
}
// Ensures that a nullable subscriptionResult has a value and has been fetched
export function isNonNullAndHasValue<T>(subscriptionResult: SubscriptionResult<T | null> | null): subscriptionResult is SubscriptionResult<T> {
return !!(subscriptionResult !== null && subscriptionResult.value !== null);
}
// Maps a "single" data subscription's events (like guild-metadata)
interface SingleEventMappingParams<T, UE extends keyof Connectable, CE extends keyof Conflictable> {
updatedEventName: UE;
updatedEventArgsMap: (...args: Arguments<Connectable[UE]>) => T;
@ -78,6 +69,8 @@ interface SingleEventMappingParams<T, UE extends keyof Connectable, CE extends k
conflictEventArgsMap: (...args: Arguments<Conflictable[CE]>) => T;
}
// Maps a "multiple" data subscription's events (like channels)
// This data subscription can be incrementally updated
interface MultipleEventMappingParams<
T,
NE extends keyof Connectable,
@ -105,6 +98,9 @@ interface MultipleEventMappingParams<
* @param fetchFunc Function that can be called to fetch the data for the subscription. This function will be called automatically if it is changed.
* Typically, this function will be set up in a useCallback with a dependency on at least the guild.
* If the fetch function returns null, this will not call "onFetch". This allows results to stay until the guilds are updated.
* @returns [
* fetchRetryCallable Can be called to re-fetch the data
* ]
*/
function useGuildSubscriptionEffect<T>(
subscriptionParams: EffectParams<T>,
@ -158,6 +154,10 @@ function useGuildSubscriptionEffect<T>(
* @param guild The guild to listen for changes on
* @param eventMappingParams The events to use to listen for changes (such as updates and conflicts)
* @param fetchFunc The function to call to fetch initial data
* @returns [
* lastResult: The last result of the fetch (null = not fetched yet)
* fetchError: The error from the fetch
* ]
*/
function useSingleGuildSubscription<T, UE extends keyof Connectable, CE extends keyof Conflictable>(
guild: CombinedGuild,
@ -219,7 +219,16 @@ function useSingleGuildSubscription<T, UE extends keyof Connectable, CE extends
return [ lastResult, fetchError ];
}
/**
* @param guild The current guild
* @param eventMappingParams The mappings to bind to guild events for updates, conflicts, etc
* @param fetchFunc The function to load the initial data
* @returns [
* fetchRetryCallable: Can be called to re-fetch
* lastResult: The last result of the fetch (null = not fetched yet)
* fetchError: The error from the fetch
* ]
*/
function useMultipleGuildSubscription<
T extends { id: string },
NE extends keyof Connectable,
@ -336,6 +345,26 @@ function useMultipleGuildSubscription<
return [ fetchRetryCallable, lastResult, fetchError ];
}
/**
* @param guild The current guild
* @param eventMappingParams The mappings to bind to guild events for updates, conflicts, etc
* @param maxElements The maximum number of elements to have loaded in the result list
* @param maxFetchElements The maximum number of elements to load in a single fetch
* @param fetchFunc The function to load the initial data
* @param fetchAboveFunc The function to load data above a reference element
* @param fetchBelowFunc The function to load data below a refrence element
* @param scrollToBottomFunc A function that will scroll the UI to the bottom (called when the data is first fetched)
* @returns [
* fetchRetryCallable: Can be called to re-fetch
* fetchAboveCallable: Call to fetch elements above the top element
* fetchBelowCallable: Call to fetch elements below the bottom element
* setScrollRatio: Call to set the position of the scrollbar. This is used to determine which elements to delete if a bunch of new elements are added.
* lastResult: The last result of the fetch (null = not fetched yet)
* fetchError: The error from the fetch
* fetchAboveError: The error from fetching above
* fetchBelowError: The error from fetching below
* ]
*/
function useMultipleGuildSubscriptionScrolling<
T extends { id: string },
NE extends keyof Connectable,
@ -673,6 +702,13 @@ function useMultipleGuildSubscriptionScrolling<
];
}
/**
* @param guild The guild to load from
* @returns [
* guildMetaResult: The guild's metadata
* fetchError: Any error from fetching
* ]
*/
export function useGuildMetadataSubscription(guild: CombinedGuild) {
const fetchMetadataFunc = useCallback(async () => {
//LOG.silly('fetching metadata for subscription');
@ -686,6 +722,15 @@ export function useGuildMetadataSubscription(guild: CombinedGuild) {
}, fetchMetadataFunc);
}
/**
* @param guild The guild to load from
* @param resourceId The resource id to load from
* @param resourceIdGuild The guild associated with the resource id to load from (if this does not match the guild to load from, this will not update)
* @returns [
* resourceResult: The resource
* fetchError: Any error from fetching
* ]
*/
export function useResourceSubscription(guild: CombinedGuild, resourceId: string | null, resourceIdGuild: CombinedGuild | null) {
const fetchResourceFunc = useCallback(async () => {
//LOG.silly('fetching resource for subscription (resourceId: ' + resourceId + ')');
@ -704,6 +749,16 @@ export function useResourceSubscription(guild: CombinedGuild, resourceId: string
}, fetchResourceFunc);
}
/**
* @param guild The guild to load from
* @param resourceId The resource id to load from
* @param resourceIdGuild The guild associated with the resource id to load from (if this does not match the guild to load from, this will not update)
* @returns [
* imgSrc: The image src (for use in an <img>)
* resourceResult: The resource
* fetchError: Any error from fetching
* ]
*/
export function useSoftImageSrcResourceSubscription(guild: CombinedGuild, resourceId: string | null, resourceIdGuild: CombinedGuild | null): [
imgSrc: string,
resourceResult: SubscriptionResult<Resource> | null,
@ -724,7 +779,14 @@ export function useSoftImageSrcResourceSubscription(guild: CombinedGuild, resour
return [ imgSrc, resourceResult, fetchError ];
}
/**
* @param guild The guild to load from
* @returns [
* channelsResult: The guild's channels
* fetchError: Any error from fetching
* ]
*/
export function useChannelsSubscription(guild: CombinedGuild) {
const fetchChannelsFunc = useCallback(async () => {
return await guild.fetchChannels();
@ -742,6 +804,13 @@ export function useChannelsSubscription(guild: CombinedGuild) {
}, fetchChannelsFunc);
}
/**
* @param guild The guild to load from
* @returns [
* membersResult: The guild's members
* fetchError: Any error from fetching
* ]
*/
export function useMembersSubscription(guild: CombinedGuild) {
const fetchMembersFunc = useCallback(async () => {
const members = await guild.fetchMembers();
@ -761,6 +830,13 @@ export function useMembersSubscription(guild: CombinedGuild) {
}, fetchMembersFunc);
}
/**
* @param guild The guild to load from
* @returns [
* selfMemberResult The guild's self member
* TODO: __fetchError: Any error from fetching
* ]
*/
export function useSelfMemberSubscription(guild: CombinedGuild): [
selfMemberResult: SubscriptionResult<Member | null> | null
] {
@ -785,6 +861,13 @@ export function useSelfMemberSubscription(guild: CombinedGuild): [
return [ selfMemberResult ];
}
/**
* @param guild The guild to load from
* @returns [
* tokensResult: The guild's tokens
* fetchError: Any error from fetching
* ]
*/
export function useTokensSubscription(guild: CombinedGuild) {
const fetchTokensFunc = useCallback(async () => {
//LOG.silly('fetching tokens for subscription');
@ -803,6 +886,22 @@ export function useTokensSubscription(guild: CombinedGuild) {
}, fetchTokensFunc);
}
/**
* @param guild The guild to load from
* @param channel The channel to load from
* @param channelGuild The guild that the channel belongs to
* @param scrollToBottomFunc A function that will be called when the UI should scroll to the bottom (when the messages are loaded for the first time for the channel)
* @returns [
* fetchRetryCallable: Can be called to re-fetch
* fetchAboveCallable: Call to fetch elements above the top element
* fetchBelowCallable: Call to fetch elements below the bottom element
* setScrollRatio: Call to set the position of the scrollbar. This is used to determine which elements to delete if a bunch of new elements are added.
* lastResult: The last result of the fetch (null = not fetched yet)
* fetchError: The error from the fetch
* fetchAboveError: The error from fetching above
* fetchBelowError: The error from fetching below
* ]
*/
export function useMessagesScrollingSubscription(guild: CombinedGuild, channel: Channel, channelGuild: CombinedGuild, scrollToBottomFunc: () => void) {
const maxFetchElements = Globals.MESSAGES_PER_REQUEST;
const maxElements = Globals.MAX_CURRENT_MESSAGES;

View File

@ -3,6 +3,7 @@ import * as uuid from 'uuid';
import CombinedGuild from '../../guild-combined';
import GuildsManager from "../../guilds-manager";
// Subscribes to changes in the list of guilds in the manager
export function useGuildListSubscription(guildsManager: GuildsManager): [ guilds: CombinedGuild[] ] {
const [ refreshId, setRefreshId ] = useState<string>(uuid.v4());

View File

@ -14,6 +14,7 @@ import ElementsUtil, { IAlignment } from './elements-util';
import React from 'react';
import FileDropTarget from '../components/file-drop-target';
/** Returns a ref that is true if the component is mounted and false otherwise. Very useful for async stuff */
export function useIsMountedRef() {
const isMounted = useRef<boolean>(false);
useEffect(() => {
@ -23,6 +24,7 @@ export function useIsMountedRef() {
return isMounted;
}
/** Returns a boolean state that will be true for a specified duration after the doShake function is called */
function useShake(ms: number): [ shaking: boolean, doShake: () => void ] {
const isMounted = useIsMountedRef();
@ -73,6 +75,7 @@ export function useOneTimeAsyncAction<T, V>(
return [ value, error ];
}
/** Creates a callable async function that will not double-trigger and gives a result, error message, and shaking boolean */
export function useAsyncCallback<ResultType>(
actionFunc: (isMounted: MutableRefObject<boolean>) => Promise<{ errorMessage: string | null, result: ResultType | null }>,
deps: DependencyList
@ -109,6 +112,7 @@ export function useAsyncCallback<ResultType>(
return [ callable, result, errorMessage, shaking ];
}
/** Creates a callable async function that does not need to return a value (or shake) */
export function useAsyncVoidCallback(
actionFunc: (isMounted: MutableRefObject<boolean>) => Promise<void>,
deps: DependencyList
@ -123,8 +127,7 @@ export function useAsyncVoidCallback(
return [ callable ];
}
// TODO: If this function gets expanded one more time, downloadSrc needs to be changed to fetchBuff and only
// be a function. Having 3 types for downloadSrc is a bit iffy.
/** Creates useful state for a button intended to be clicked to download something */
export function useDownloadButton(
downloadName: string,
fetchBuff: () => Promise<Buffer | null>,
@ -209,6 +212,7 @@ export function useDownloadButton(
return [ callable, text, shaking ];
}
/** Creates useful state variables for a button intended call a submit action */
export function useAsyncSubmitButton<ResultType>(
actionFunc: (isMounted: MutableRefObject<boolean>) => Promise<{ errorMessage: string | null, result: ResultType | null }>,
deps: DependencyList,
@ -250,6 +254,7 @@ export function useAsyncSubmitButton<ResultType>(
return [ callable, text, shaking, result, errorMessage ];
}
/** Creates useful state for an infinite scroll subscription */
export function useColumnReverseInfiniteScroll(
threshold: number,
ends: { hasMoreBelow: boolean, hasMoreAbove: boolean } | null,
@ -317,6 +322,7 @@ export function useColumnReverseInfiniteScroll(
}
// Makes sure to also allow you to fly-out a click starting inside of the ref'd element but was dragged outside
/** Calls the close action when you hit escape or click outside of the ref element */
export function useCloseWhenEscapeOrClickedOrContextOutsideEffect(ref: RefObject<HTMLElement>, close: () => void) {
// Have to use a ref here and not states since we can't re-assign state between mouseup and click
const mouseRef = useRef<{ mouseDownTarget: Node | null, mouseUpTarget: Node | null}>({ mouseDownTarget: null, mouseUpTarget: null });
@ -404,6 +410,7 @@ export function useCloseWhenEscapeOrClickedOrContextOutsideEffect(ref: RefObject
}, [ handleKeyDown ]);
}
/** Aligns an element to a reference element or position every time realignDeps changes */
export function useAlignment(
rootRef: RefObject<HTMLElement>,
relativeToRef: RefObject<HTMLElement | null> | null,
@ -431,6 +438,7 @@ export function useAlignment(
return [ className ];
}
/** Creates useful context menu state */
export function useContextMenu(
createContextMenu: (close: () => void) => ReactNode,
createContextMenuDeps: DependencyList
@ -460,6 +468,7 @@ export function useContextMenu(
return [ isOpen ? contextMenu : null, toggle, close, open, isOpen ];
}
/** Creates context menu state for right-clicks */
export function useContextClickContextMenu(
createContextMenu: (alignment: IAlignment, relativeToPos: { x: number, y: number }, close: () => void) => ReactNode,
createContextMenuDeps: DependencyList
@ -485,6 +494,7 @@ export function useContextClickContextMenu(
return [ contextMenu, onContextMenu ];
}
/** Creates context state for hovering over an element */
export function useContextHover(
createContextHover: () => ReactNode,
createContextHoverDeps: DependencyList
@ -509,6 +519,7 @@ export function useContextHover(
return [ isOpen ? contextHover : null, mouseEnterCallable, mouseLeaveCallable ];
}
/** Creates a drop target element that will appear when you drag a file over the document */
export function useDocumentDropTarget(
message: string,
setBuff: Dispatch<SetStateAction<Buffer | null>>,
@ -544,3 +555,4 @@ export function useDocumentDropTarget(
return [ dropTarget ];
}

View File

@ -1,6 +1,7 @@
import { GuildMetadata, Member, Channel, Message, Resource, Token } from "./data-types";
import { Fetchable, GuaranteedFetchable } from "./guild-types";
/** Ensures that the fetchable actually returns a value (sanity check class and fixes types) */
export default class EnsuredFetchable implements GuaranteedFetchable {
constructor(
private fetchable: Fetchable

View File

@ -9,6 +9,7 @@ import { AutoVerifier, AutoVerifierChangesType } from './auto-verifier';
import { AutoVerifierWithArg, PartialMessageListQuery, IDQuery } from './auto-verifier-with-args';
import { EventEmitter } from 'tsee';
/** Uses a primary fetchable and trusted fetchable and verifys the results. Can be chained together with itself as a trusted fetchable! */
export default class PairVerifierFetchable extends EventEmitter<Conflictable> implements AsyncFetchable {
private readonly fetchMetadataVerifier: AutoVerifier<GuildMetadata>;

View File

@ -20,6 +20,7 @@ import { EventEmitter } from 'tsee';
import { AutoVerifierChangesType } from './auto-verifier';
import { IDQuery, PartialMessageListQuery } from './auto-verifier-with-args';
/** The general guild class. This handles connecting a RAM, PersonalDB, and Socket guild together using fetchable-pair-verifiers and some manual caching to nicely update messages */
export default class CombinedGuild extends EventEmitter<Connectable & Conflictable> implements AsyncGuaranteedFetchable, AsyncRequestable {
private readonly ramGuild: RAMGuild;
private readonly personalDBGuild: PersonalDBGuild;

View File

@ -4,6 +4,7 @@ import { Channel, GuildMetadata, GuildMetadataLocal, Member, Message, Resource,
import PersonalDB from "./personal-db";
import { AutoVerifierWithArg, PartialMessageListQuery } from './auto-verifier-with-args';
/** A guild connected to a local Sqlite database */
export default class PersonalDBGuild implements AsyncFetchable, AsyncLackable {
constructor(
private readonly db: PersonalDB,

View File

@ -4,6 +4,7 @@ import MessageRAMCache from "./message-ram-cache";
import ResourceRAMCache from "./resource-ram-cache";
import { AutoVerifierWithArg, PartialMessageListQuery } from './auto-verifier-with-args';
/** A guild connected to the RAM. This guild does not nescessarily implement all aspects of the guild (for example, it does not do "fetchMessagesBefore") */
export default class RAMGuild implements SyncFetchable, SyncLackable {
private metadata: GuildMetadata | null = null;
private members = new Map<string, Member>(); // id -> member

View File

@ -14,6 +14,7 @@ import SocketVerifier from './socket-verifier';
import { EventEmitter } from 'tsee';
// Note: you should not be calling the eventemitter functions on outside classes
/** A guild connected to a socket.io socket server */
export default class SocketGuild extends EventEmitter<Connectable> implements AsyncGuaranteedFetchable, AsyncRequestable {
private queryDedups = new Map<string, DedupAwaiter<unknown>>();

View File

@ -10,50 +10,17 @@ import * as socketio from 'socket.io-client';
import * as crypto from 'crypto';
import { Changes, Channel, GuildMetadata, GuildMetadataLocal, Member, Message, Resource, SocketConfig, Token } from './data-types';
import { GuildMetadata, GuildMetadataLocal, Member, SocketConfig } from './data-types';
import { IAddGuildData } from './elements/overlays/overlay-add-guild';
import { EventEmitter } from 'tsee';
import CombinedGuild from './guild-combined';
import PersonalDB from './personal-db';
import MessageRAMCache from './message-ram-cache';
import ResourceRAMCache from './resource-ram-cache';
import { GuildEventNames } from './guild-types';
import { AutoVerifierChangesType } from './auto-verifier';
import { IDQuery, PartialMessageListQuery } from './auto-verifier-with-args';
/** Manages all guilds that the user is connected to */
export default class GuildsManager extends EventEmitter<{
'update-guilds': () => void;
'connect': (guild: CombinedGuild) => void;
'disconnect': (guild: CombinedGuild) => void;
'verified': (guild: CombinedGuild) => void;
'update-metadata': (guild: CombinedGuild, guildMeta: GuildMetadata) => void;
'new-channels': (guild: CombinedGuild, channels: Channel[]) => void;
'update-channels': (guild: CombinedGuild, updatedChannels: Channel[]) => void;
'remove-channels': (guild: CombinedGuild, removedChannels: Channel[]) => void;
'new-members': (guild: CombinedGuild, members: Member[]) => void;
'update-members': (guild: CombinedGuild, updatedMembers: Member[]) => void;
'remove-members': (guild: CombinedGuild, removedMembers: Member[]) => void;
'new-messages': (guild: CombinedGuild, messages: Message[]) => void;
'update-messages': (guild: CombinedGuild, updatedMessages: Message[]) => void;
'remove-messages': (guild: CombinedGuild, removedMessages: Message[]) => void;
'update-resource': (guild: CombinedGuild, updatedResource: Resource) => void;
'new-tokens': (guild: CombinedGuild, newTokens: Token[]) => void;
'update-tokens': (guild: CombinedGuild, updatedTokens: Token[]) => void;
'remove-tokens': (guild: CombinedGuild, removedTokens: Token[]) => void;
'conflict-metadata': (guild: CombinedGuild, changesType: AutoVerifierChangesType, oldGuildMeta: GuildMetadata, newGuildMeta: GuildMetadata) => void;
'conflict-channels': (guild: CombinedGuild, changesType: AutoVerifierChangesType, changes: Changes<Channel>) => void;
'conflict-members': (guild: CombinedGuild, changesType: AutoVerifierChangesType, changes: Changes<Member>) => void;
'conflict-tokens': (guild: CombinedGuild, changesType: AutoVerifierChangesType, changes: Changes<Token>) => void;
'conflict-messages': (guild: CombinedGuild, query: PartialMessageListQuery, changesType: AutoVerifierChangesType, changes: Changes<Message>) => void;
'conflict-resource': (guild: CombinedGuild, query: IDQuery, changesType: AutoVerifierChangesType, oldResource: Resource, newResource: Resource) => void;
}> {
public guilds: CombinedGuild[] = [];
@ -81,13 +48,6 @@ export default class GuildsManager extends EventEmitter<{
this.guilds.push(guild);
this.emit('update-guilds');
// Forward guild events through this event emitter
for (const eventName of GuildEventNames) {
guild.on(eventName as any, (...args: any) => {
this.emit(eventName as any, guild, ...args);
});
}
return guild;
}

View File

@ -4,15 +4,17 @@ import Globals from "./globals";
import * as electronRemote from '@electron/remote';
const electronConsole = electronRemote.getGlobal('console') as Console;
import Logger from '../../logger/logger';
import { AutoVerifierWithArg, PartialMessageListQuery } from "./auto-verifier-with-args";
const LOG = Logger.create(__filename, electronConsole);
import { AutoVerifierWithArg, PartialMessageListQuery } from "./auto-verifier-with-args";
interface MessagesWithMetadata {
messages: Map<string, Message>;
totalCharacters: number;
lastUsed: Date;
}
/** Stores messages by-channel-by-guild in RAM */
export default class MessageRAMCache {
// g#guildId/c#channelId -> list of messages
private data = new Map<string, MessagesWithMetadata>();

View File

@ -12,6 +12,7 @@ import * as sqlite3 from 'sqlite3';
import { Channel, GuildMetadataLocal, Member, Message, Resource, SocketConfig } from "./data-types";
/** Sqlite representation of a guild */
export default class PersonalDB {
private transactions = new ConcurrentQueue(1);

View File

@ -2,6 +2,7 @@ import Globals from './globals';
import { Resource, ShouldNeverHappenError } from './data-types';
/** Stores a limited size of resources per-guild in a LRU cache */
export default class ResourceRAMCache {
private data = new Map<string, { resource: Resource, lastUsed: Date }>(); // (guildId, resourceId) -> { resource, lastUsed }
private size = 0;

View File

@ -23,6 +23,7 @@ export default class Util {
}
}
/** Get rid of potentially illegal characters in a file name string */
static sanitize(name: string): string {
// Windows Version (created for Windows, most likely works cross-platform too given my research)
// Allowed Characters: Extended Unicode Charset (1-255)
@ -62,8 +63,9 @@ export default class Util {
return availableBaseName + ext;
}
/** Waits a number of MS before continuing */
static async sleep(ms: number): Promise<unknown> {
return await new Promise((resolve, reject) => {
return await new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
@ -105,6 +107,7 @@ export default class Util {
});
}
/** Randomly selects one element from the list using a uniform distribution */
static randomChoice<T>(list: T[]): T {
return list[Math.floor(Math.random() * list.length)] as T;
}