2022-01-29 23:05:12 +00:00
import * as electronRemote from '@electron/remote' ;
const electronConsole = electronRemote . getGlobal ( 'console' ) as Console ;
2022-10-03 03:55:57 +00:00
import Logger from '../../../../logger/logger' ;
2022-01-29 23:05:12 +00:00
const LOG = Logger . create ( __filename , electronConsole ) ;
2022-01-27 01:57:29 +00:00
import { ReactNode } from 'react' ;
2022-01-29 23:05:12 +00:00
import { atom , selector } from 'recoil' ;
2022-01-27 06:40:29 +00:00
import { Channel , GuildMetadata , Member , Message } from '../../data-types' ;
import CombinedGuild from '../../guild-combined' ;
import GuildsManager from '../../guilds-manager' ;
2022-01-27 01:57:29 +00:00
2022-01-29 23:05:12 +00:00
export type WithUnloadedValue = {
value : undefined ;
hasValueError : false ;
valueError : undefined ;
} ;
export type WithLoadedValue < T > = T extends undefined ? never : {
2022-01-27 01:57:29 +00:00
value : T ;
2022-01-29 23:05:12 +00:00
hasValueError : false ;
valueError : undefined ;
} ;
export type WithFailedValue = {
value : undefined ;
hasValueError : true ;
valueError : unknown ;
} ;
export type WithLoadableValue < T > = WithUnloadedValue | WithLoadedValue < T > | WithFailedValue ;
// NOTE: Should not extend this type. It is for type guards
export type GuildWithValue < T > = WithLoadedValue < T > & { guild : CombinedGuild } ;
export type GuildWithErrorableValue < T > = WithLoadableValue < T > & { guild : CombinedGuild } ;
export type ChannelWithErrorableValue < T > = WithLoadableValue < T > & { channel : Channel } ;
// Devoid is when we don't have a selected guild yet.
export function isDevoid < T > ( state : NonNullable < T > | null ) : state is null {
return state === null ;
2022-01-27 01:57:29 +00:00
}
2022-01-29 23:05:12 +00:00
// NOTE: Using "pended" instead of "pending" since it has 6 letters like the rest of these functions. It will make the code look nicer :)
export function isPended < T > ( withLoadableValue : WithLoadableValue < T > ) : withLoadableValue is WithUnloadedValue {
return withLoadableValue . value === undefined && withLoadableValue . hasValueError === false ;
}
export function isLoaded < T > ( withLoadableValue : WithLoadableValue < T > ) : withLoadableValue is WithLoadedValue < T > {
return withLoadableValue . value !== undefined ;
}
export function isFailed < T > ( withLoadableValue : WithLoadableValue < T > ) : withLoadableValue is WithFailedValue {
return withLoadableValue . hasValueError ;
2022-01-27 01:57:29 +00:00
}
2022-02-03 05:05:38 +00:00
// WARNING: This must NOT be used in a place that could prevent hooks from running
2022-01-29 23:05:12 +00:00
// NOTE: This is useful to skip rendering elements that require an atom if its value is not available yet. Using this in the parent element will prevent the child eleemnt from
// needing to implement the complexity of loading without a value
export function isDevoidPendedOrFailed < T > ( withNullableLoadableValue : WithLoadableValue < T > | null ) : withNullableLoadableValue is null | WithUnloadedValue | WithFailedValue {
return withNullableLoadableValue === null || isPended ( withNullableLoadableValue ) || isFailed ( withNullableLoadableValue ) ;
2022-01-27 01:57:29 +00:00
}
export const overlayState = atom < ReactNode > ( {
key : 'overlayState' ,
default : null
} ) ;
2022-01-27 06:40:29 +00:00
export const guildsManagerState = atom < GuildsManager | null > ( {
key : 'guildsManagerState' ,
default : null ,
// Allow mutability so that we can have changes to internal guild stuff
// We will still listen to new/update/delete/conflict events to maintain the UI's state
dangerouslyAllowMutability : true
2022-01-29 23:05:12 +00:00
} ) ;
2022-01-27 06:40:29 +00:00
2022-01-27 01:57:29 +00:00
export const guildsState = atom < GuildWithErrorableValue < GuildMetadata > [ ] | null > ( {
key : 'guildsState' ,
2022-01-27 06:40:29 +00:00
default : null ,
dangerouslyAllowMutability : true
2022-01-27 01:57:29 +00:00
} ) ;
export const selectedGuildIdState = atom < number | null > ( {
key : 'selectedGuildIdState' ,
default : null
} ) ;
export const selectedGuildWithMetaState = selector < GuildWithErrorableValue < GuildMetadata > | null > ( {
key : 'selectedGuildWithMetaState' ,
get : ( { get } ) = > {
const guildsWithMeta = get ( guildsState ) ;
if ( guildsWithMeta === null ) return null ;
const guildId = get ( selectedGuildIdState ) ;
if ( guildId === null ) return null ;
return guildsWithMeta . find ( guildWithMeta = > guildWithMeta . guild . id === guildId ) ? ? null ;
2022-01-27 06:40:29 +00:00
} ,
dangerouslyAllowMutability : true
2022-01-27 01:57:29 +00:00
} ) ;
2022-01-29 23:05:12 +00:00
// Note: You likely want selectedGuildWithMetaState
2022-01-27 01:57:29 +00:00
export const selectedGuildState = selector < CombinedGuild | null > ( {
key : 'selectedGuildState' ,
get : ( { get } ) = > {
const guildWithMeta = get ( selectedGuildWithMetaState ) ;
2022-01-29 23:05:12 +00:00
if ( isDevoid ( guildWithMeta ) ) return null ;
2022-01-27 01:57:29 +00:00
return guildWithMeta . guild ;
2022-01-27 06:40:29 +00:00
} ,
dangerouslyAllowMutability : true
2022-01-27 01:57:29 +00:00
} ) ;
2022-01-29 23:05:12 +00:00
export const selectedGuildWithMembersState = atom < GuildWithErrorableValue < Member [ ] > | null > ( {
key : 'selectedGuildWithMembersState' ,
default : null ,
dangerouslyAllowMutability : true
} ) ;
export const selectedGuildWithSelfMemberState = selector < GuildWithErrorableValue < Member > | null > ( {
key : 'selectedGuildWithSelfMemberState' ,
get : ( { get } ) = > {
const guildWithMembers = get ( selectedGuildWithMembersState ) ;
if ( isDevoid ( guildWithMembers ) ) return null ;
if ( isFailed ( guildWithMembers ) ) return { guild : guildWithMembers.guild , value : undefined , hasValueError : true , valueError : 'members fetch error' } ;
if ( isPended ( guildWithMembers ) ) return { guild : guildWithMembers.guild , value : undefined , hasValueError : false , valueError : undefined } ;
const selfMember = guildWithMembers . value . find ( member = > member . id === guildWithMembers . guild . memberId ) ? ? null ;
if ( selfMember === null ) {
LOG . warn ( 'unable to find self member in members' ) ;
return { guild : guildWithMembers.guild , value : undefined , hasValueError : true , valueError : 'unable to find self in members' } ;
}
return { guild : guildWithMembers.guild , value : selfMember , hasValueError : false , valueError : undefined } ;
} ,
dangerouslyAllowMutability : true
2022-01-27 01:57:29 +00:00
} ) ;
export const selectedGuildWithChannelsState = atom < GuildWithErrorableValue < Channel [ ] > | null > ( {
key : 'selectedGuildChannelsState' ,
2022-01-29 23:05:12 +00:00
default : null ,
dangerouslyAllowMutability : true
2022-01-27 01:57:29 +00:00
} ) ;
export const selectedGuildWithActiveChannelIdState = atom < GuildWithValue < string | null > | null > ( {
key : 'selectedGuildWithActiveChannelIdState' ,
2022-01-29 23:05:12 +00:00
default : null ,
dangerouslyAllowMutability : true
2022-01-27 01:57:29 +00:00
} ) ;
2022-01-29 23:05:12 +00:00
export const selectedGuildWithActiveChannelMessagesState = atom < GuildWithValue < ChannelWithErrorableValue < Message [ ] > > | null > ( {
2022-01-27 01:57:29 +00:00
key : 'selectedGuildWithActiveChannelMessagesState' ,
2022-01-29 23:05:12 +00:00
default : null ,
dangerouslyAllowMutability : true
2022-01-27 01:57:29 +00:00
} ) ;
2022-01-29 23:05:12 +00:00
// TODO: selectedGuildWithActiveChannelState
2022-01-27 01:57:29 +00:00