add a touch more documentation in the code
This commit is contained in:
parent
9ca2a22881
commit
a0f25158d3
@ -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";
|
||||
|
@ -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: [] };
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 (#)
|
||||
|
@ -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 },
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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 ];
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>;
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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>>();
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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>();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user