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';
|
//import * as electronRemote from '@electron/remote';
|
||||||
const electronConsole = electronRemote.getGlobal('console') as Console;
|
//const electronConsole = electronRemote.getGlobal('console') as Console;
|
||||||
import Logger from '../../logger/logger';
|
//import Logger from '../../logger/logger';
|
||||||
const LOG = Logger.create(__filename, electronConsole);
|
//const LOG = Logger.create(__filename, electronConsole);
|
||||||
|
|
||||||
import { AutoVerifier, AutoVerifierChangesType } from "./auto-verifier";
|
import { AutoVerifier, AutoVerifierChangesType } from "./auto-verifier";
|
||||||
import { Changes, WithEquals } from "./data-types";
|
import { Changes, WithEquals } from "./data-types";
|
||||||
|
@ -38,6 +38,7 @@ export class AutoVerifier<T> {
|
|||||||
private verifyFunc: (primaryResult: T | null, trustedResult: T | null) => Promise<boolean>
|
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> {
|
static getChanges<T extends WithEquals<T> & { id: string }>(primaryResult: T[] | null, trustedResult: T[] | null): Changes<T> {
|
||||||
const changes: Changes<T> = { added: [], updated: [], deleted: [] };
|
const changes: Changes<T> = { added: [], updated: [], deleted: [] };
|
||||||
|
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
import * as electronRemote from '@electron/remote';
|
// Runs the promise unless it is already running
|
||||||
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
|
|
||||||
export default class DedupAwaiter<T> {
|
export default class DedupAwaiter<T> {
|
||||||
private promise: Promise<T> | null = null;
|
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';
|
import React from 'react';
|
||||||
|
|
||||||
export interface HTMLElementWithRemoveSelf extends HTMLElement {
|
// This file contains JSX elements that can be used in place directly of storing the elements on the disk.
|
||||||
removeSelf: (() => void);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class BaseElements {
|
export default class BaseElements {
|
||||||
// Scraped directly from discord (#)
|
// Scraped directly from discord (#)
|
||||||
|
@ -12,6 +12,8 @@ import * as uuid from 'uuid';
|
|||||||
import Util from '../../util';
|
import Util from '../../util';
|
||||||
import CombinedGuild from '../../guild-combined';
|
import CombinedGuild from '../../guild-combined';
|
||||||
|
|
||||||
|
// This class contains general element management functions
|
||||||
|
|
||||||
export interface IAlignment {
|
export interface IAlignment {
|
||||||
left?: string;
|
left?: string;
|
||||||
centerX?: string;
|
centerX?: string;
|
||||||
@ -52,6 +54,7 @@ export default class ElementsUtil {
|
|||||||
setState(old => !start);
|
setState(old => !start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Gets a string compatible with an <img> src attribute */
|
||||||
static async getImageBufferSrc(buffer: Buffer): Promise<string> {
|
static async getImageBufferSrc(buffer: Buffer): Promise<string> {
|
||||||
const result = await FileType.fromBuffer(buffer);
|
const result = await FileType.fromBuffer(buffer);
|
||||||
switch (result && result.mime) {
|
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> {
|
static async getImageSrcFromBufferFailSoftly(buff: Buffer | null): Promise<string> {
|
||||||
if (buff === null) {
|
if (buff === null) {
|
||||||
return './img/loading.svg';
|
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 <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 {
|
static parseMessageText(text: string): JSX.Element {
|
||||||
const obj: SimpleQElement = { tag: 'span', content: [], class: null };
|
const obj: SimpleQElement = { tag: 'span', content: [], class: null };
|
||||||
const stack: SimpleQElement[] = [ obj ];
|
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
|
// 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
|
// 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
|
// relativeTo can be an { x, y } or HTMLElement
|
||||||
|
/** Aligns an element relative to another element or a position in the window */
|
||||||
static alignContextElement(
|
static alignContextElement(
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
relativeTo: HTMLElement | { x: number, y: number },
|
relativeTo: HTMLElement | { x: number, y: number },
|
||||||
|
@ -16,6 +16,7 @@ import Globals from '../../globals';
|
|||||||
import ElementsUtil from './elements-util';
|
import ElementsUtil from './elements-util';
|
||||||
|
|
||||||
// Abuse closures to get state in case it changed after an await call
|
// 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 {
|
function getStateAfterAwait<T>(setState: Dispatch<SetStateAction<T>>): T {
|
||||||
let x: unknown;
|
let x: unknown;
|
||||||
setState(state => {
|
setState(state => {
|
||||||
@ -25,22 +26,7 @@ function getStateAfterAwait<T>(setState: Dispatch<SetStateAction<T>>): T {
|
|||||||
return x as T;
|
return x as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SingleSubscriptionEvents = {
|
// Parameters used by base guildSubscription to fetch the initial value
|
||||||
'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;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EffectParams<T> {
|
interface EffectParams<T> {
|
||||||
guild: CombinedGuild;
|
guild: CombinedGuild;
|
||||||
// TODO: I changed this from value: T | null to just value: T. I think
|
// TODO: I changed this from value: T | null to just value: T. I think
|
||||||
@ -52,13 +38,16 @@ interface EffectParams<T> {
|
|||||||
unbindEventsFunc: () => void;
|
unbindEventsFunc: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// General typescript type that infers the arguments of a function
|
||||||
type Arguments<T> = T extends (...args: infer A) => unknown ? A : never;
|
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> {
|
export interface SubscriptionResult<T> {
|
||||||
value: T;
|
value: T;
|
||||||
guild: CombinedGuild;
|
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> {
|
export interface ScrollingSubscriptionResult<T> {
|
||||||
value: {
|
value: {
|
||||||
ends: { hasMoreAbove: boolean, hasMoreBelow: boolean };
|
ends: { hasMoreAbove: boolean, hasMoreBelow: boolean };
|
||||||
@ -67,10 +56,12 @@ export interface ScrollingSubscriptionResult<T> {
|
|||||||
guild: CombinedGuild;
|
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> {
|
export function isNonNullAndHasValue<T>(subscriptionResult: SubscriptionResult<T | null> | null): subscriptionResult is SubscriptionResult<T> {
|
||||||
return !!(subscriptionResult !== null && subscriptionResult.value !== null);
|
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> {
|
interface SingleEventMappingParams<T, UE extends keyof Connectable, CE extends keyof Conflictable> {
|
||||||
updatedEventName: UE;
|
updatedEventName: UE;
|
||||||
updatedEventArgsMap: (...args: Arguments<Connectable[UE]>) => T;
|
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;
|
conflictEventArgsMap: (...args: Arguments<Conflictable[CE]>) => T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Maps a "multiple" data subscription's events (like channels)
|
||||||
|
// This data subscription can be incrementally updated
|
||||||
interface MultipleEventMappingParams<
|
interface MultipleEventMappingParams<
|
||||||
T,
|
T,
|
||||||
NE extends keyof Connectable,
|
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.
|
* @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.
|
* 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.
|
* 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>(
|
function useGuildSubscriptionEffect<T>(
|
||||||
subscriptionParams: EffectParams<T>,
|
subscriptionParams: EffectParams<T>,
|
||||||
@ -158,6 +154,10 @@ function useGuildSubscriptionEffect<T>(
|
|||||||
* @param guild The guild to listen for changes on
|
* @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 eventMappingParams The events to use to listen for changes (such as updates and conflicts)
|
||||||
* @param fetchFunc The function to call to fetch initial data
|
* @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>(
|
function useSingleGuildSubscription<T, UE extends keyof Connectable, CE extends keyof Conflictable>(
|
||||||
guild: CombinedGuild,
|
guild: CombinedGuild,
|
||||||
@ -219,7 +219,16 @@ function useSingleGuildSubscription<T, UE extends keyof Connectable, CE extends
|
|||||||
|
|
||||||
return [ lastResult, fetchError ];
|
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<
|
function useMultipleGuildSubscription<
|
||||||
T extends { id: string },
|
T extends { id: string },
|
||||||
NE extends keyof Connectable,
|
NE extends keyof Connectable,
|
||||||
@ -336,6 +345,26 @@ function useMultipleGuildSubscription<
|
|||||||
return [ fetchRetryCallable, lastResult, fetchError ];
|
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<
|
function useMultipleGuildSubscriptionScrolling<
|
||||||
T extends { id: string },
|
T extends { id: string },
|
||||||
NE extends keyof Connectable,
|
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) {
|
export function useGuildMetadataSubscription(guild: CombinedGuild) {
|
||||||
const fetchMetadataFunc = useCallback(async () => {
|
const fetchMetadataFunc = useCallback(async () => {
|
||||||
//LOG.silly('fetching metadata for subscription');
|
//LOG.silly('fetching metadata for subscription');
|
||||||
@ -686,6 +722,15 @@ export function useGuildMetadataSubscription(guild: CombinedGuild) {
|
|||||||
}, fetchMetadataFunc);
|
}, 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) {
|
export function useResourceSubscription(guild: CombinedGuild, resourceId: string | null, resourceIdGuild: CombinedGuild | null) {
|
||||||
const fetchResourceFunc = useCallback(async () => {
|
const fetchResourceFunc = useCallback(async () => {
|
||||||
//LOG.silly('fetching resource for subscription (resourceId: ' + resourceId + ')');
|
//LOG.silly('fetching resource for subscription (resourceId: ' + resourceId + ')');
|
||||||
@ -704,6 +749,16 @@ export function useResourceSubscription(guild: CombinedGuild, resourceId: string
|
|||||||
}, fetchResourceFunc);
|
}, 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): [
|
export function useSoftImageSrcResourceSubscription(guild: CombinedGuild, resourceId: string | null, resourceIdGuild: CombinedGuild | null): [
|
||||||
imgSrc: string,
|
imgSrc: string,
|
||||||
resourceResult: SubscriptionResult<Resource> | null,
|
resourceResult: SubscriptionResult<Resource> | null,
|
||||||
@ -725,6 +780,13 @@ export function useSoftImageSrcResourceSubscription(guild: CombinedGuild, resour
|
|||||||
return [ imgSrc, resourceResult, fetchError ];
|
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) {
|
export function useChannelsSubscription(guild: CombinedGuild) {
|
||||||
const fetchChannelsFunc = useCallback(async () => {
|
const fetchChannelsFunc = useCallback(async () => {
|
||||||
return await guild.fetchChannels();
|
return await guild.fetchChannels();
|
||||||
@ -742,6 +804,13 @@ export function useChannelsSubscription(guild: CombinedGuild) {
|
|||||||
}, fetchChannelsFunc);
|
}, fetchChannelsFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param guild The guild to load from
|
||||||
|
* @returns [
|
||||||
|
* membersResult: The guild's members
|
||||||
|
* fetchError: Any error from fetching
|
||||||
|
* ]
|
||||||
|
*/
|
||||||
export function useMembersSubscription(guild: CombinedGuild) {
|
export function useMembersSubscription(guild: CombinedGuild) {
|
||||||
const fetchMembersFunc = useCallback(async () => {
|
const fetchMembersFunc = useCallback(async () => {
|
||||||
const members = await guild.fetchMembers();
|
const members = await guild.fetchMembers();
|
||||||
@ -761,6 +830,13 @@ export function useMembersSubscription(guild: CombinedGuild) {
|
|||||||
}, fetchMembersFunc);
|
}, 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): [
|
export function useSelfMemberSubscription(guild: CombinedGuild): [
|
||||||
selfMemberResult: SubscriptionResult<Member | null> | null
|
selfMemberResult: SubscriptionResult<Member | null> | null
|
||||||
] {
|
] {
|
||||||
@ -785,6 +861,13 @@ export function useSelfMemberSubscription(guild: CombinedGuild): [
|
|||||||
return [ selfMemberResult ];
|
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) {
|
export function useTokensSubscription(guild: CombinedGuild) {
|
||||||
const fetchTokensFunc = useCallback(async () => {
|
const fetchTokensFunc = useCallback(async () => {
|
||||||
//LOG.silly('fetching tokens for subscription');
|
//LOG.silly('fetching tokens for subscription');
|
||||||
@ -803,6 +886,22 @@ export function useTokensSubscription(guild: CombinedGuild) {
|
|||||||
}, fetchTokensFunc);
|
}, 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) {
|
export function useMessagesScrollingSubscription(guild: CombinedGuild, channel: Channel, channelGuild: CombinedGuild, scrollToBottomFunc: () => void) {
|
||||||
const maxFetchElements = Globals.MESSAGES_PER_REQUEST;
|
const maxFetchElements = Globals.MESSAGES_PER_REQUEST;
|
||||||
const maxElements = Globals.MAX_CURRENT_MESSAGES;
|
const maxElements = Globals.MAX_CURRENT_MESSAGES;
|
||||||
|
@ -3,6 +3,7 @@ import * as uuid from 'uuid';
|
|||||||
import CombinedGuild from '../../guild-combined';
|
import CombinedGuild from '../../guild-combined';
|
||||||
import GuildsManager from "../../guilds-manager";
|
import GuildsManager from "../../guilds-manager";
|
||||||
|
|
||||||
|
// Subscribes to changes in the list of guilds in the manager
|
||||||
export function useGuildListSubscription(guildsManager: GuildsManager): [ guilds: CombinedGuild[] ] {
|
export function useGuildListSubscription(guildsManager: GuildsManager): [ guilds: CombinedGuild[] ] {
|
||||||
const [ refreshId, setRefreshId ] = useState<string>(uuid.v4());
|
const [ refreshId, setRefreshId ] = useState<string>(uuid.v4());
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import ElementsUtil, { IAlignment } from './elements-util';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import FileDropTarget from '../components/file-drop-target';
|
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() {
|
export function useIsMountedRef() {
|
||||||
const isMounted = useRef<boolean>(false);
|
const isMounted = useRef<boolean>(false);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -23,6 +24,7 @@ export function useIsMountedRef() {
|
|||||||
return isMounted;
|
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 ] {
|
function useShake(ms: number): [ shaking: boolean, doShake: () => void ] {
|
||||||
const isMounted = useIsMountedRef();
|
const isMounted = useIsMountedRef();
|
||||||
|
|
||||||
@ -73,6 +75,7 @@ export function useOneTimeAsyncAction<T, V>(
|
|||||||
return [ value, error ];
|
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>(
|
export function useAsyncCallback<ResultType>(
|
||||||
actionFunc: (isMounted: MutableRefObject<boolean>) => Promise<{ errorMessage: string | null, result: ResultType | null }>,
|
actionFunc: (isMounted: MutableRefObject<boolean>) => Promise<{ errorMessage: string | null, result: ResultType | null }>,
|
||||||
deps: DependencyList
|
deps: DependencyList
|
||||||
@ -109,6 +112,7 @@ export function useAsyncCallback<ResultType>(
|
|||||||
return [ callable, result, errorMessage, shaking ];
|
return [ callable, result, errorMessage, shaking ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Creates a callable async function that does not need to return a value (or shake) */
|
||||||
export function useAsyncVoidCallback(
|
export function useAsyncVoidCallback(
|
||||||
actionFunc: (isMounted: MutableRefObject<boolean>) => Promise<void>,
|
actionFunc: (isMounted: MutableRefObject<boolean>) => Promise<void>,
|
||||||
deps: DependencyList
|
deps: DependencyList
|
||||||
@ -123,8 +127,7 @@ export function useAsyncVoidCallback(
|
|||||||
return [ callable ];
|
return [ callable ];
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: If this function gets expanded one more time, downloadSrc needs to be changed to fetchBuff and only
|
/** Creates useful state for a button intended to be clicked to download something */
|
||||||
// be a function. Having 3 types for downloadSrc is a bit iffy.
|
|
||||||
export function useDownloadButton(
|
export function useDownloadButton(
|
||||||
downloadName: string,
|
downloadName: string,
|
||||||
fetchBuff: () => Promise<Buffer | null>,
|
fetchBuff: () => Promise<Buffer | null>,
|
||||||
@ -209,6 +212,7 @@ export function useDownloadButton(
|
|||||||
return [ callable, text, shaking ];
|
return [ callable, text, shaking ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Creates useful state variables for a button intended call a submit action */
|
||||||
export function useAsyncSubmitButton<ResultType>(
|
export function useAsyncSubmitButton<ResultType>(
|
||||||
actionFunc: (isMounted: MutableRefObject<boolean>) => Promise<{ errorMessage: string | null, result: ResultType | null }>,
|
actionFunc: (isMounted: MutableRefObject<boolean>) => Promise<{ errorMessage: string | null, result: ResultType | null }>,
|
||||||
deps: DependencyList,
|
deps: DependencyList,
|
||||||
@ -250,6 +254,7 @@ export function useAsyncSubmitButton<ResultType>(
|
|||||||
return [ callable, text, shaking, result, errorMessage ];
|
return [ callable, text, shaking, result, errorMessage ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Creates useful state for an infinite scroll subscription */
|
||||||
export function useColumnReverseInfiniteScroll(
|
export function useColumnReverseInfiniteScroll(
|
||||||
threshold: number,
|
threshold: number,
|
||||||
ends: { hasMoreBelow: boolean, hasMoreAbove: boolean } | null,
|
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
|
// 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) {
|
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
|
// 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 });
|
const mouseRef = useRef<{ mouseDownTarget: Node | null, mouseUpTarget: Node | null}>({ mouseDownTarget: null, mouseUpTarget: null });
|
||||||
@ -404,6 +410,7 @@ export function useCloseWhenEscapeOrClickedOrContextOutsideEffect(ref: RefObject
|
|||||||
}, [ handleKeyDown ]);
|
}, [ handleKeyDown ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Aligns an element to a reference element or position every time realignDeps changes */
|
||||||
export function useAlignment(
|
export function useAlignment(
|
||||||
rootRef: RefObject<HTMLElement>,
|
rootRef: RefObject<HTMLElement>,
|
||||||
relativeToRef: RefObject<HTMLElement | null> | null,
|
relativeToRef: RefObject<HTMLElement | null> | null,
|
||||||
@ -431,6 +438,7 @@ export function useAlignment(
|
|||||||
return [ className ];
|
return [ className ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Creates useful context menu state */
|
||||||
export function useContextMenu(
|
export function useContextMenu(
|
||||||
createContextMenu: (close: () => void) => ReactNode,
|
createContextMenu: (close: () => void) => ReactNode,
|
||||||
createContextMenuDeps: DependencyList
|
createContextMenuDeps: DependencyList
|
||||||
@ -460,6 +468,7 @@ export function useContextMenu(
|
|||||||
return [ isOpen ? contextMenu : null, toggle, close, open, isOpen ];
|
return [ isOpen ? contextMenu : null, toggle, close, open, isOpen ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Creates context menu state for right-clicks */
|
||||||
export function useContextClickContextMenu(
|
export function useContextClickContextMenu(
|
||||||
createContextMenu: (alignment: IAlignment, relativeToPos: { x: number, y: number }, close: () => void) => ReactNode,
|
createContextMenu: (alignment: IAlignment, relativeToPos: { x: number, y: number }, close: () => void) => ReactNode,
|
||||||
createContextMenuDeps: DependencyList
|
createContextMenuDeps: DependencyList
|
||||||
@ -485,6 +494,7 @@ export function useContextClickContextMenu(
|
|||||||
return [ contextMenu, onContextMenu ];
|
return [ contextMenu, onContextMenu ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Creates context state for hovering over an element */
|
||||||
export function useContextHover(
|
export function useContextHover(
|
||||||
createContextHover: () => ReactNode,
|
createContextHover: () => ReactNode,
|
||||||
createContextHoverDeps: DependencyList
|
createContextHoverDeps: DependencyList
|
||||||
@ -509,6 +519,7 @@ export function useContextHover(
|
|||||||
return [ isOpen ? contextHover : null, mouseEnterCallable, mouseLeaveCallable ];
|
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(
|
export function useDocumentDropTarget(
|
||||||
message: string,
|
message: string,
|
||||||
setBuff: Dispatch<SetStateAction<Buffer | null>>,
|
setBuff: Dispatch<SetStateAction<Buffer | null>>,
|
||||||
@ -544,3 +555,4 @@ export function useDocumentDropTarget(
|
|||||||
|
|
||||||
return [ dropTarget ];
|
return [ dropTarget ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { GuildMetadata, Member, Channel, Message, Resource, Token } from "./data-types";
|
import { GuildMetadata, Member, Channel, Message, Resource, Token } from "./data-types";
|
||||||
import { Fetchable, GuaranteedFetchable } from "./guild-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 {
|
export default class EnsuredFetchable implements GuaranteedFetchable {
|
||||||
constructor(
|
constructor(
|
||||||
private fetchable: Fetchable
|
private fetchable: Fetchable
|
||||||
|
@ -9,6 +9,7 @@ import { AutoVerifier, AutoVerifierChangesType } from './auto-verifier';
|
|||||||
import { AutoVerifierWithArg, PartialMessageListQuery, IDQuery } from './auto-verifier-with-args';
|
import { AutoVerifierWithArg, PartialMessageListQuery, IDQuery } from './auto-verifier-with-args';
|
||||||
import { EventEmitter } from 'tsee';
|
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 {
|
export default class PairVerifierFetchable extends EventEmitter<Conflictable> implements AsyncFetchable {
|
||||||
|
|
||||||
private readonly fetchMetadataVerifier: AutoVerifier<GuildMetadata>;
|
private readonly fetchMetadataVerifier: AutoVerifier<GuildMetadata>;
|
||||||
|
@ -20,6 +20,7 @@ import { EventEmitter } from 'tsee';
|
|||||||
import { AutoVerifierChangesType } from './auto-verifier';
|
import { AutoVerifierChangesType } from './auto-verifier';
|
||||||
import { IDQuery, PartialMessageListQuery } from './auto-verifier-with-args';
|
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 {
|
export default class CombinedGuild extends EventEmitter<Connectable & Conflictable> implements AsyncGuaranteedFetchable, AsyncRequestable {
|
||||||
private readonly ramGuild: RAMGuild;
|
private readonly ramGuild: RAMGuild;
|
||||||
private readonly personalDBGuild: PersonalDBGuild;
|
private readonly personalDBGuild: PersonalDBGuild;
|
||||||
|
@ -4,6 +4,7 @@ import { Channel, GuildMetadata, GuildMetadataLocal, Member, Message, Resource,
|
|||||||
import PersonalDB from "./personal-db";
|
import PersonalDB from "./personal-db";
|
||||||
import { AutoVerifierWithArg, PartialMessageListQuery } from './auto-verifier-with-args';
|
import { AutoVerifierWithArg, PartialMessageListQuery } from './auto-verifier-with-args';
|
||||||
|
|
||||||
|
/** A guild connected to a local Sqlite database */
|
||||||
export default class PersonalDBGuild implements AsyncFetchable, AsyncLackable {
|
export default class PersonalDBGuild implements AsyncFetchable, AsyncLackable {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly db: PersonalDB,
|
private readonly db: PersonalDB,
|
||||||
|
@ -4,6 +4,7 @@ import MessageRAMCache from "./message-ram-cache";
|
|||||||
import ResourceRAMCache from "./resource-ram-cache";
|
import ResourceRAMCache from "./resource-ram-cache";
|
||||||
import { AutoVerifierWithArg, PartialMessageListQuery } from './auto-verifier-with-args';
|
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 {
|
export default class RAMGuild implements SyncFetchable, SyncLackable {
|
||||||
private metadata: GuildMetadata | null = null;
|
private metadata: GuildMetadata | null = null;
|
||||||
private members = new Map<string, Member>(); // id -> member
|
private members = new Map<string, Member>(); // id -> member
|
||||||
|
@ -14,6 +14,7 @@ import SocketVerifier from './socket-verifier';
|
|||||||
import { EventEmitter } from 'tsee';
|
import { EventEmitter } from 'tsee';
|
||||||
|
|
||||||
// Note: you should not be calling the eventemitter functions on outside classes
|
// 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 {
|
export default class SocketGuild extends EventEmitter<Connectable> implements AsyncGuaranteedFetchable, AsyncRequestable {
|
||||||
private queryDedups = new Map<string, DedupAwaiter<unknown>>();
|
private queryDedups = new Map<string, DedupAwaiter<unknown>>();
|
||||||
|
|
||||||
|
@ -10,50 +10,17 @@ import * as socketio from 'socket.io-client';
|
|||||||
|
|
||||||
import * as crypto from 'crypto';
|
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 { IAddGuildData } from './elements/overlays/overlay-add-guild';
|
||||||
import { EventEmitter } from 'tsee';
|
import { EventEmitter } from 'tsee';
|
||||||
import CombinedGuild from './guild-combined';
|
import CombinedGuild from './guild-combined';
|
||||||
import PersonalDB from './personal-db';
|
import PersonalDB from './personal-db';
|
||||||
import MessageRAMCache from './message-ram-cache';
|
import MessageRAMCache from './message-ram-cache';
|
||||||
import ResourceRAMCache from './resource-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<{
|
export default class GuildsManager extends EventEmitter<{
|
||||||
'update-guilds': () => void;
|
'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[] = [];
|
public guilds: CombinedGuild[] = [];
|
||||||
|
|
||||||
@ -81,13 +48,6 @@ export default class GuildsManager extends EventEmitter<{
|
|||||||
this.guilds.push(guild);
|
this.guilds.push(guild);
|
||||||
this.emit('update-guilds');
|
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;
|
return guild;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,15 +4,17 @@ import Globals from "./globals";
|
|||||||
import * as electronRemote from '@electron/remote';
|
import * as electronRemote from '@electron/remote';
|
||||||
const electronConsole = electronRemote.getGlobal('console') as Console;
|
const electronConsole = electronRemote.getGlobal('console') as Console;
|
||||||
import Logger from '../../logger/logger';
|
import Logger from '../../logger/logger';
|
||||||
import { AutoVerifierWithArg, PartialMessageListQuery } from "./auto-verifier-with-args";
|
|
||||||
const LOG = Logger.create(__filename, electronConsole);
|
const LOG = Logger.create(__filename, electronConsole);
|
||||||
|
|
||||||
|
import { AutoVerifierWithArg, PartialMessageListQuery } from "./auto-verifier-with-args";
|
||||||
|
|
||||||
interface MessagesWithMetadata {
|
interface MessagesWithMetadata {
|
||||||
messages: Map<string, Message>;
|
messages: Map<string, Message>;
|
||||||
totalCharacters: number;
|
totalCharacters: number;
|
||||||
lastUsed: Date;
|
lastUsed: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Stores messages by-channel-by-guild in RAM */
|
||||||
export default class MessageRAMCache {
|
export default class MessageRAMCache {
|
||||||
// g#guildId/c#channelId -> list of messages
|
// g#guildId/c#channelId -> list of messages
|
||||||
private data = new Map<string, MessagesWithMetadata>();
|
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";
|
import { Channel, GuildMetadataLocal, Member, Message, Resource, SocketConfig } from "./data-types";
|
||||||
|
|
||||||
|
/** Sqlite representation of a guild */
|
||||||
export default class PersonalDB {
|
export default class PersonalDB {
|
||||||
private transactions = new ConcurrentQueue(1);
|
private transactions = new ConcurrentQueue(1);
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import Globals from './globals';
|
|||||||
|
|
||||||
import { Resource, ShouldNeverHappenError } from './data-types';
|
import { Resource, ShouldNeverHappenError } from './data-types';
|
||||||
|
|
||||||
|
/** Stores a limited size of resources per-guild in a LRU cache */
|
||||||
export default class ResourceRAMCache {
|
export default class ResourceRAMCache {
|
||||||
private data = new Map<string, { resource: Resource, lastUsed: Date }>(); // (guildId, resourceId) -> { resource, lastUsed }
|
private data = new Map<string, { resource: Resource, lastUsed: Date }>(); // (guildId, resourceId) -> { resource, lastUsed }
|
||||||
private size = 0;
|
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 {
|
static sanitize(name: string): string {
|
||||||
// Windows Version (created for Windows, most likely works cross-platform too given my research)
|
// Windows Version (created for Windows, most likely works cross-platform too given my research)
|
||||||
// Allowed Characters: Extended Unicode Charset (1-255)
|
// Allowed Characters: Extended Unicode Charset (1-255)
|
||||||
@ -62,8 +63,9 @@ export default class Util {
|
|||||||
return availableBaseName + ext;
|
return availableBaseName + ext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Waits a number of MS before continuing */
|
||||||
static async sleep(ms: number): Promise<unknown> {
|
static async sleep(ms: number): Promise<unknown> {
|
||||||
return await new Promise((resolve, reject) => {
|
return await new Promise((resolve) => {
|
||||||
setTimeout(resolve, ms);
|
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 {
|
static randomChoice<T>(list: T[]): T {
|
||||||
return list[Math.floor(Math.random() * list.length)] as T;
|
return list[Math.floor(Math.random() * list.length)] as T;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user