2024-05-14 08:45:41 +02:00
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
2024-07-23 22:50:23 +02:00
import {
2024-08-15 03:40:41 +02:00
getLayoutModelForTabById ,
2024-07-23 22:50:23 +02:00
LayoutTreeActionType ,
LayoutTreeInsertNodeAction ,
newLayoutNode ,
2024-08-15 03:40:41 +02:00
} from "@/layout/index" ;
2024-10-17 23:34:02 +02:00
import { getLayoutModelForStaticTab } from "@/layout/lib/layoutModelHooks" ;
2024-09-12 03:03:55 +02:00
import { getWebServerEndpoint } from "@/util/endpoints" ;
2024-08-22 00:04:39 +02:00
import { fetch } from "@/util/fetchutil" ;
2024-09-12 03:03:55 +02:00
import { getPrefixedSettings , isBlank } from "@/util/util" ;
2024-10-10 19:12:42 +02:00
import { atom , Atom , PrimitiveAtom , useAtomValue } from "jotai" ;
import { globalStore } from "./jotaiStore" ;
2024-07-30 20:44:19 +02:00
import { modalsModel } from "./modalmodel" ;
2024-09-12 03:03:55 +02:00
import { ClientService , ObjectService } from "./services" ;
2024-05-27 22:59:58 +02:00
import * as WOS from "./wos" ;
2024-09-12 03:03:55 +02:00
import { getFileSubject , waveEventSubscribe } from "./wps" ;
2024-05-14 08:45:41 +02:00
2024-07-09 08:13:12 +02:00
let PLATFORM : NodeJS.Platform = "darwin" ;
2024-07-19 22:44:32 +02:00
let atoms : GlobalAtomsType ;
let globalEnvironment : "electron" | "renderer" ;
2024-09-05 09:21:08 +02:00
const blockComponentModelMap = new Map < string , BlockComponentModel > ( ) ;
2024-08-22 00:49:23 +02:00
const Counters = new Map < string , number > ( ) ;
2024-09-12 03:03:55 +02:00
const ConnStatusMap = new Map < string , PrimitiveAtom < ConnStatus > > ( ) ;
2024-07-19 22:44:32 +02:00
type GlobalInitOptions = {
2024-10-17 23:34:02 +02:00
tabId : string ;
2024-07-19 22:44:32 +02:00
platform : NodeJS.Platform ;
windowId : string ;
clientId : string ;
environment : "electron" | "renderer" ;
} ;
function initGlobal ( initOpts : GlobalInitOptions ) {
globalEnvironment = initOpts . environment ;
setPlatform ( initOpts . platform ) ;
initGlobalAtoms ( initOpts ) ;
}
2024-07-09 08:13:12 +02:00
function setPlatform ( platform : NodeJS.Platform ) {
PLATFORM = platform ;
}
2024-07-19 22:44:32 +02:00
function initGlobalAtoms ( initOpts : GlobalInitOptions ) {
2024-09-12 03:03:55 +02:00
const windowIdAtom = atom ( initOpts . windowId ) as PrimitiveAtom < string > ;
const clientIdAtom = atom ( initOpts . clientId ) as PrimitiveAtom < string > ;
const uiContextAtom = atom ( ( get ) = > {
2024-07-19 22:44:32 +02:00
const uiContext : UIContext = {
2024-10-17 23:34:02 +02:00
windowid : initOpts.windowId ,
activetabid : initOpts.tabId ,
2024-07-19 22:44:32 +02:00
} ;
return uiContext ;
2024-09-12 03:03:55 +02:00
} ) as Atom < UIContext > ;
2024-07-19 22:44:32 +02:00
2024-09-12 03:03:55 +02:00
const isFullScreenAtom = atom ( false ) as PrimitiveAtom < boolean > ;
2024-07-22 22:33:10 +02:00
try {
getApi ( ) . onFullScreenChange ( ( isFullScreen ) = > {
globalStore . set ( isFullScreenAtom , isFullScreen ) ;
} ) ;
} catch ( _ ) {
// do nothing
}
2024-08-20 00:49:40 +02:00
try {
getApi ( ) . onMenuItemAbout ( ( ) = > {
modalsModel . pushModal ( "AboutModal" ) ;
} ) ;
} catch ( _ ) {
// do nothing
}
2024-09-12 03:03:55 +02:00
const clientAtom : Atom < Client > = atom ( ( get ) = > {
2024-07-19 22:44:32 +02:00
const clientId = get ( clientIdAtom ) ;
if ( clientId == null ) {
return null ;
}
return WOS . getObjectValue ( WOS . makeORef ( "client" , clientId ) , get ) ;
} ) ;
2024-09-12 03:03:55 +02:00
const windowDataAtom : Atom < WaveWindow > = atom ( ( get ) = > {
2024-07-19 22:44:32 +02:00
const windowId = get ( windowIdAtom ) ;
if ( windowId == null ) {
return null ;
}
const rtn = WOS . getObjectValue < WaveWindow > ( WOS . makeORef ( "window" , windowId ) , get ) ;
return rtn ;
} ) ;
2024-09-12 03:03:55 +02:00
const workspaceAtom : Atom < Workspace > = atom ( ( get ) = > {
2024-07-19 22:44:32 +02:00
const windowData = get ( windowDataAtom ) ;
if ( windowData == null ) {
return null ;
}
return WOS . getObjectValue ( WOS . makeORef ( "workspace" , windowData . workspaceid ) , get ) ;
} ) ;
2024-09-12 03:03:55 +02:00
const fullConfigAtom = atom ( null ) as PrimitiveAtom < FullConfigType > ;
const settingsAtom = atom ( ( get ) = > {
2024-08-28 03:49:49 +02:00
return get ( fullConfigAtom ) ? . settings ? ? { } ;
2024-09-12 03:03:55 +02:00
} ) as Atom < SettingsType > ;
const tabAtom : Atom < Tab > = atom ( ( get ) = > {
2024-10-17 23:34:02 +02:00
return WOS . getObjectValue ( WOS . makeORef ( "tab" , initOpts . tabId ) , get ) ;
2024-07-19 22:44:32 +02:00
} ) ;
2024-10-17 23:34:02 +02:00
const staticTabIdAtom : Atom < string > = atom ( ( get ) = > {
return initOpts . tabId ;
2024-07-19 22:44:32 +02:00
} ) ;
2024-09-12 03:03:55 +02:00
const controlShiftDelayAtom = atom ( false ) ;
const updaterStatusAtom = atom < UpdaterStatus > ( "up-to-date" ) as PrimitiveAtom < UpdaterStatus > ;
2024-08-06 20:05:26 +02:00
try {
2024-08-15 06:47:09 +02:00
globalStore . set ( updaterStatusAtom , getApi ( ) . getUpdaterStatus ( ) ) ;
2024-08-06 20:05:26 +02:00
getApi ( ) . onUpdaterStatusChange ( ( status ) = > {
2024-08-15 06:47:09 +02:00
globalStore . set ( updaterStatusAtom , status ) ;
2024-08-06 20:05:26 +02:00
} ) ;
} catch ( _ ) {
// do nothing
}
2024-09-07 01:20:27 +02:00
2024-09-12 03:03:55 +02:00
const reducedMotionSettingAtom = atom ( ( get ) = > get ( settingsAtom ) ? . [ "window:reducedmotion" ] ) ;
const reducedMotionSystemPreferenceAtom = atom ( false ) ;
2024-09-07 01:20:27 +02:00
// Composite of the prefers-reduced-motion media query and the window:reducedmotion user setting.
2024-09-12 03:03:55 +02:00
const prefersReducedMotionAtom = atom ( ( get ) = > {
2024-09-07 01:20:27 +02:00
const reducedMotionSetting = get ( reducedMotionSettingAtom ) ;
const reducedMotionSystemPreference = get ( reducedMotionSystemPreferenceAtom ) ;
return reducedMotionSetting || reducedMotionSystemPreference ;
} ) ;
// Set up a handler for changes to the prefers-reduced-motion media query.
if ( globalThis . window != null ) {
const reducedMotionQuery = window . matchMedia ( "(prefers-reduced-motion: reduce)" ) ;
globalStore . set ( reducedMotionSystemPreferenceAtom , ! reducedMotionQuery || reducedMotionQuery . matches ) ;
reducedMotionQuery ? . addEventListener ( "change" , ( ) = > {
globalStore . set ( reducedMotionSystemPreferenceAtom , reducedMotionQuery . matches ) ;
} ) ;
}
2024-09-12 03:03:55 +02:00
const typeAheadModalAtom = atom ( { } ) ;
const modalOpen = atom ( false ) ;
const allConnStatusAtom = atom < ConnStatus [ ] > ( ( get ) = > {
2024-09-06 08:09:30 +02:00
const connStatuses = Array . from ( ConnStatusMap . values ( ) ) . map ( ( atom ) = > get ( atom ) ) ;
return connStatuses ;
} ) ;
2024-09-13 01:02:18 +02:00
const flashErrorsAtom = atom < FlashErrorType [ ] > ( [ ] ) ;
2024-11-16 06:26:16 +01:00
const notificationsAtom = atom < NotificationType [ ] > ( [ ] ) ;
const notificationPopoverModeAtom = atom < boolean > ( false ) ;
2024-10-28 07:01:47 +01:00
const reinitVersion = atom ( 0 ) ;
2024-07-19 22:44:32 +02:00
atoms = {
// initialized in wave.ts (will not be null inside of application)
clientId : clientIdAtom ,
uiContext : uiContextAtom ,
client : clientAtom ,
waveWindow : windowDataAtom ,
workspace : workspaceAtom ,
2024-08-28 03:49:49 +02:00
fullConfigAtom ,
settingsAtom ,
2024-08-15 06:47:09 +02:00
tabAtom ,
2024-10-17 23:34:02 +02:00
staticTabId : staticTabIdAtom ,
2024-07-22 22:33:10 +02:00
isFullScreen : isFullScreenAtom ,
2024-08-15 06:47:09 +02:00
controlShiftDelayAtom ,
updaterStatusAtom ,
2024-09-07 01:20:27 +02:00
prefersReducedMotionAtom ,
2024-08-23 09:18:49 +02:00
typeAheadModalAtom ,
2024-08-30 01:06:15 +02:00
modalOpen ,
2024-09-06 08:09:30 +02:00
allConnStatus : allConnStatusAtom ,
2024-09-13 01:02:18 +02:00
flashErrors : flashErrorsAtom ,
2024-11-16 06:26:16 +01:00
notifications : notificationsAtom ,
notificationPopoverMode : notificationPopoverModeAtom ,
2024-10-28 07:01:47 +01:00
reinitVersion ,
2024-05-27 22:59:58 +02:00
} ;
2024-07-19 22:44:32 +02:00
}
2024-05-14 08:45:41 +02:00
2024-09-12 03:03:55 +02:00
function initGlobalWaveEventSubs() {
waveEventSubscribe (
{
eventType : "waveobj:update" ,
handler : ( event ) = > {
// console.log("waveobj:update wave event handler", event);
const update : WaveObjUpdate = event . data ;
WOS . updateWaveObject ( update ) ;
} ,
} ,
{
eventType : "config" ,
handler : ( event ) = > {
// console.log("config wave event handler", event);
const fullConfig = ( event . data as WatcherUpdate ) . fullconfig ;
globalStore . set ( atoms . fullConfigAtom , fullConfig ) ;
} ,
} ,
{
eventType : "userinput" ,
handler : ( event ) = > {
// console.log("userinput event handler", event);
const data : UserInputRequest = event . data ;
modalsModel . pushModal ( "UserInputModal" , { . . . data } ) ;
} ,
} ,
{
eventType : "blockfile" ,
handler : ( event ) = > {
// console.log("blockfile event update", event);
const fileData : WSFileEventData = event . data ;
const fileSubject = getFileSubject ( fileData . zoneid , fileData . filename ) ;
if ( fileSubject != null ) {
fileSubject . next ( fileData ) ;
}
} ,
2024-08-24 03:12:40 +02:00
}
2024-09-12 03:03:55 +02:00
) ;
2024-06-14 08:54:04 +02:00
}
2024-05-28 23:24:36 +02:00
const blockCache = new Map < string , Map < string , any > > ( ) ;
function useBlockCache < T > ( blockId : string , name : string , makeFn : ( ) = > T ) : T {
let blockMap = blockCache . get ( blockId ) ;
if ( blockMap == null ) {
blockMap = new Map < string , any > ( ) ;
blockCache . set ( blockId , blockMap ) ;
}
let value = blockMap . get ( name ) ;
if ( value == null ) {
value = makeFn ( ) ;
blockMap . set ( name , value ) ;
}
return value as T ;
}
2024-10-19 02:07:44 +02:00
function getBlockMetaKeyAtom < T extends keyof MetaType > ( blockId : string , key : T ) : Atom < MetaType [ T ] > {
const blockCache = getSingleBlockAtomCache ( blockId ) ;
const metaAtomName = "#meta-" + key ;
let metaAtom = blockCache . get ( metaAtomName ) ;
if ( metaAtom != null ) {
return metaAtom ;
}
metaAtom = atom ( ( get ) = > {
let blockAtom = WOS . getWaveObjectAtom ( WOS . makeORef ( "block" , blockId ) ) ;
let blockData = get ( blockAtom ) ;
return blockData ? . meta ? . [ key ] ;
} ) ;
blockCache . set ( metaAtomName , metaAtom ) ;
return metaAtom ;
}
function useBlockMetaKeyAtom < T extends keyof MetaType > ( blockId : string , key : T ) : MetaType [ T ] {
return useAtomValue ( getBlockMetaKeyAtom ( blockId , key ) ) ;
}
2024-09-12 03:03:55 +02:00
const settingsAtomCache = new Map < string , Atom < any > > ( ) ;
2024-06-21 22:23:07 +02:00
2024-11-21 19:44:16 +01:00
function getOverrideConfigAtom < T extends keyof SettingsType > ( blockId : string , key : T ) : Atom < SettingsType [ T ] > {
2024-10-19 02:07:44 +02:00
const blockCache = getSingleBlockAtomCache ( blockId ) ;
const overrideAtomName = "#settingsoverride-" + key ;
let overrideAtom = blockCache . get ( overrideAtomName ) ;
if ( overrideAtom != null ) {
return overrideAtom ;
}
overrideAtom = atom ( ( get ) = > {
const blockMetaKeyAtom = getBlockMetaKeyAtom ( blockId , key as any ) ;
const metaKeyVal = get ( blockMetaKeyAtom ) ;
if ( metaKeyVal != null ) {
return metaKeyVal ;
}
const settingsKeyAtom = getSettingsKeyAtom ( key ) ;
const settingsVal = get ( settingsKeyAtom ) ;
if ( settingsVal != null ) {
return settingsVal ;
}
return null ;
} ) ;
blockCache . set ( overrideAtomName , overrideAtom ) ;
return overrideAtom ;
}
function useOverrideConfigAtom < T extends keyof SettingsType > ( blockId : string , key : T ) : SettingsType [ T ] {
2024-11-21 19:44:16 +01:00
return useAtomValue ( getOverrideConfigAtom ( blockId , key ) ) ;
2024-10-19 02:07:44 +02:00
}
2024-10-07 23:08:57 +02:00
function getSettingsKeyAtom < T extends keyof SettingsType > ( key : T ) : Atom < SettingsType [ T ] > {
2024-09-12 03:03:55 +02:00
let settingsKeyAtom = settingsAtomCache . get ( key ) as Atom < SettingsType [ T ] > ;
if ( settingsKeyAtom == null ) {
settingsKeyAtom = atom ( ( get ) = > {
2024-08-28 03:49:49 +02:00
const settings = get ( atoms . settingsAtom ) ;
2024-06-21 22:23:07 +02:00
if ( settings == null ) {
return null ;
}
2024-08-28 03:49:49 +02:00
return settings [ key ] ;
} ) ;
2024-09-12 03:03:55 +02:00
settingsAtomCache . set ( key , settingsKeyAtom ) ;
2024-06-21 22:23:07 +02:00
}
2024-09-12 03:03:55 +02:00
return settingsKeyAtom ;
2024-08-28 03:49:49 +02:00
}
2024-10-19 02:21:18 +02:00
function useSettingsKeyAtom < T extends keyof SettingsType > ( key : T ) : SettingsType [ T ] {
return useAtomValue ( getSettingsKeyAtom ( key ) ) ;
}
2024-09-12 03:03:55 +02:00
function useSettingsPrefixAtom ( prefix : string ) : Atom < SettingsType > {
2024-08-28 03:49:49 +02:00
// TODO: use a shallow equal here to make this more efficient
2024-09-12 03:03:55 +02:00
let settingsPrefixAtom = settingsAtomCache . get ( prefix + ":" ) as Atom < SettingsType > ;
if ( settingsPrefixAtom == null ) {
settingsPrefixAtom = atom ( ( get ) = > {
2024-08-28 03:49:49 +02:00
const settings = get ( atoms . settingsAtom ) ;
if ( settings == null ) {
return { } ;
}
2024-09-12 03:03:55 +02:00
return getPrefixedSettings ( settings , prefix ) ;
2024-08-28 03:49:49 +02:00
} ) ;
2024-09-12 03:03:55 +02:00
settingsAtomCache . set ( prefix + ":" , settingsPrefixAtom ) ;
2024-08-28 03:49:49 +02:00
}
2024-09-12 03:03:55 +02:00
return settingsPrefixAtom ;
2024-06-21 22:23:07 +02:00
}
2024-09-12 03:03:55 +02:00
const blockAtomCache = new Map < string , Map < string , Atom < any > >> ( ) ;
2024-05-28 00:44:57 +02:00
2024-10-19 02:07:44 +02:00
function getSingleBlockAtomCache ( blockId : string ) : Map < string , Atom < any > > {
2024-05-16 09:29:58 +02:00
let blockCache = blockAtomCache . get ( blockId ) ;
if ( blockCache == null ) {
2024-09-12 03:03:55 +02:00
blockCache = new Map < string , Atom < any > > ( ) ;
2024-05-16 09:29:58 +02:00
blockAtomCache . set ( blockId , blockCache ) ;
}
2024-10-19 02:07:44 +02:00
return blockCache ;
}
function useBlockAtom < T > ( blockId : string , name : string , makeFn : ( ) = > Atom < T > ) : Atom < T > {
const blockCache = getSingleBlockAtomCache ( blockId ) ;
2024-05-16 09:29:58 +02:00
let atom = blockCache . get ( name ) ;
if ( atom == null ) {
atom = makeFn ( ) ;
blockCache . set ( name , atom ) ;
2024-05-28 00:44:57 +02:00
console . log ( "New BlockAtom" , blockId , name ) ;
2024-05-16 09:29:58 +02:00
}
2024-09-12 03:03:55 +02:00
return atom as Atom < T > ;
2024-05-16 09:29:58 +02:00
}
2024-08-22 00:49:23 +02:00
function useBlockDataLoaded ( blockId : string ) : boolean {
const loadedAtom = useBlockAtom < boolean > ( blockId , "block-loaded" , ( ) = > {
return WOS . getWaveObjectLoadingAtom ( WOS . makeORef ( "block" , blockId ) ) ;
} ) ;
2024-09-12 03:03:55 +02:00
return useAtomValue ( loadedAtom ) ;
2024-08-24 03:12:40 +02:00
}
2024-06-19 20:20:35 +02:00
/ * *
* Get the preload api .
* /
2024-06-14 01:49:25 +02:00
function getApi ( ) : ElectronApi {
return ( window as any ) . api ;
}
2024-11-14 03:00:13 +01:00
async function createBlock ( blockDef : BlockDef , magnified = false , ephemeral = false ) : Promise < string > {
const tabId = globalStore . get ( atoms . staticTabId ) ;
const layoutModel = getLayoutModelForTabById ( tabId ) ;
2024-06-18 07:38:48 +02:00
const rtOpts : RuntimeOpts = { termsize : { rows : 25 , cols : 80 } } ;
2024-09-12 03:03:55 +02:00
const blockId = await ObjectService . CreateBlock ( blockDef , rtOpts ) ;
2024-11-14 03:00:13 +01:00
if ( ephemeral ) {
layoutModel . newEphemeralNode ( blockId ) ;
return blockId ;
}
2024-08-15 03:40:41 +02:00
const insertNodeAction : LayoutTreeInsertNodeAction = {
2024-06-18 07:38:48 +02:00
type : LayoutTreeActionType . InsertNode ,
2024-08-15 03:40:41 +02:00
node : newLayoutNode ( undefined , undefined , undefined , { blockId } ) ,
2024-08-26 20:56:00 +02:00
magnified ,
2024-08-28 08:16:07 +02:00
focused : true ,
2024-06-18 07:38:48 +02:00
} ;
2024-08-15 03:40:41 +02:00
layoutModel . treeReducer ( insertNodeAction ) ;
2024-08-20 03:28:28 +02:00
return blockId ;
2024-06-18 07:38:48 +02:00
}
// when file is not found, returns {data: null, fileInfo: null}
async function fetchWaveFile (
zoneId : string ,
fileName : string ,
offset? : number
) : Promise < { data : Uint8Array ; fileInfo : WaveFile } > {
2024-07-23 21:46:29 +02:00
const usp = new URLSearchParams ( ) ;
2024-06-18 07:38:48 +02:00
usp . set ( "zoneid" , zoneId ) ;
usp . set ( "name" , fileName ) ;
if ( offset != null ) {
usp . set ( "offset" , offset . toString ( ) ) ;
}
2024-07-18 03:49:27 +02:00
const resp = await fetch ( getWebServerEndpoint ( ) + "/wave/file?" + usp . toString ( ) ) ;
2024-06-18 07:38:48 +02:00
if ( ! resp . ok ) {
if ( resp . status === 404 ) {
return { data : null , fileInfo : null } ;
}
throw new Error ( "error getting wave file: " + resp . statusText ) ;
}
if ( resp . status == 204 ) {
return { data : null , fileInfo : null } ;
}
2024-07-23 21:46:29 +02:00
const fileInfo64 = resp . headers . get ( "X-ZoneFileInfo" ) ;
2024-06-18 07:38:48 +02:00
if ( fileInfo64 == null ) {
throw new Error ( ` missing zone file info for ${ zoneId } : ${ fileName } ` ) ;
}
2024-07-23 21:46:29 +02:00
const fileInfo = JSON . parse ( atob ( fileInfo64 ) ) ;
2024-06-18 07:38:48 +02:00
const data = await resp . arrayBuffer ( ) ;
return { data : new Uint8Array ( data ) , fileInfo } ;
}
2024-08-26 20:56:00 +02:00
function setNodeFocus ( nodeId : string ) {
2024-10-17 23:34:02 +02:00
const layoutModel = getLayoutModelForStaticTab ( ) ;
2024-08-26 20:56:00 +02:00
layoutModel . focusNode ( nodeId ) ;
2024-06-21 21:32:38 +02:00
}
2024-07-09 00:04:48 +02:00
const objectIdWeakMap = new WeakMap ( ) ;
let objectIdCounter = 0 ;
function getObjectId ( obj : any ) : number {
if ( ! objectIdWeakMap . has ( obj ) ) {
objectIdWeakMap . set ( obj , objectIdCounter ++ ) ;
}
return objectIdWeakMap . get ( obj ) ;
}
2024-08-01 22:46:06 +02:00
let cachedIsDev : boolean = null ;
function isDev() {
if ( cachedIsDev == null ) {
cachedIsDev = getApi ( ) . getIsDev ( ) ;
}
return cachedIsDev ;
}
2024-08-28 03:49:49 +02:00
let cachedUserName : string = null ;
function getUserName ( ) : string {
if ( cachedUserName == null ) {
cachedUserName = getApi ( ) . getUserName ( ) ;
}
return cachedUserName ;
}
2024-09-06 03:54:12 +02:00
let cachedHostName : string = null ;
function getHostName ( ) : string {
if ( cachedHostName == null ) {
cachedHostName = getApi ( ) . getHostName ( ) ;
}
return cachedHostName ;
}
2024-08-29 22:37:05 +02:00
/ * *
* Open a link in a new window , or in a new web widget . The user can set all links to open in a new web widget using the ` web:openlinksinternally ` setting .
* @param uri The link to open .
* @param forceOpenInternally Force the link to open in a new web widget .
* /
async function openLink ( uri : string , forceOpenInternally = false ) {
if ( forceOpenInternally || globalStore . get ( atoms . settingsAtom ) ? . [ "web:openlinksinternally" ] ) {
2024-08-07 23:27:16 +02:00
const blockDef : BlockDef = {
meta : {
view : "web" ,
url : uri ,
} ,
} ;
await createBlock ( blockDef ) ;
} else {
getApi ( ) . openExternal ( uri ) ;
}
}
2024-09-05 09:21:08 +02:00
function registerBlockComponentModel ( blockId : string , bcm : BlockComponentModel ) {
blockComponentModelMap . set ( blockId , bcm ) ;
2024-08-22 00:49:23 +02:00
}
2024-09-05 09:21:08 +02:00
function unregisterBlockComponentModel ( blockId : string ) {
blockComponentModelMap . delete ( blockId ) ;
2024-08-22 00:49:23 +02:00
}
2024-09-05 09:21:08 +02:00
function getBlockComponentModel ( blockId : string ) : BlockComponentModel {
return blockComponentModelMap . get ( blockId ) ;
2024-08-22 00:49:23 +02:00
}
2024-10-25 08:16:44 +02:00
function getFocusedBlockId ( ) : string {
const layoutModel = getLayoutModelForStaticTab ( ) ;
const focusedLayoutNode = globalStore . get ( layoutModel . focusedNode ) ;
return focusedLayoutNode ? . data ? . blockId ;
}
// pass null to refocus the currently focused block
2024-09-03 01:48:10 +02:00
function refocusNode ( blockId : string ) {
if ( blockId == null ) {
2024-10-25 08:16:44 +02:00
blockId = getFocusedBlockId ( ) ;
if ( blockId == null ) {
return ;
}
2024-09-03 01:48:10 +02:00
}
2024-10-17 23:34:02 +02:00
const layoutModel = getLayoutModelForStaticTab ( ) ;
2024-09-03 01:48:10 +02:00
const layoutNodeId = layoutModel . getNodeByBlockId ( blockId ) ;
if ( layoutNodeId ? . id == null ) {
return ;
}
layoutModel . focusNode ( layoutNodeId . id ) ;
2024-09-05 09:21:08 +02:00
const bcm = getBlockComponentModel ( blockId ) ;
const ok = bcm ? . viewModel ? . giveFocus ? . ( ) ;
2024-09-03 01:48:10 +02:00
if ( ! ok ) {
const inputElem = document . getElementById ( ` ${ blockId } -dummy-focus ` ) ;
inputElem ? . focus ( ) ;
}
}
2024-08-22 00:49:23 +02:00
function countersClear() {
Counters . clear ( ) ;
}
function counterInc ( name : string , incAmt : number = 1 ) {
let count = Counters . get ( name ) ? ? 0 ;
count += incAmt ;
Counters . set ( name , count ) ;
}
function countersPrint() {
let outStr = "" ;
for ( const [ name , count ] of Counters . entries ( ) ) {
outStr += ` ${ name } : ${ count } \ n ` ;
}
console . log ( outStr ) ;
}
2024-08-24 03:12:40 +02:00
async function loadConnStatus() {
2024-09-12 03:03:55 +02:00
const connStatusArr = await ClientService . GetAllConnStatus ( ) ;
2024-08-24 03:12:40 +02:00
if ( connStatusArr == null ) {
return ;
}
for ( const connStatus of connStatusArr ) {
const curAtom = getConnStatusAtom ( connStatus . connection ) ;
globalStore . set ( curAtom , connStatus ) ;
}
}
function subscribeToConnEvents() {
2024-09-12 03:03:55 +02:00
waveEventSubscribe ( {
eventType : "connchange" ,
handler : ( event : WaveEvent ) = > {
try {
const connStatus = event . data as ConnStatus ;
if ( connStatus == null || isBlank ( connStatus . connection ) ) {
return ;
}
console . log ( "connstatus update" , connStatus ) ;
let curAtom = getConnStatusAtom ( connStatus . connection ) ;
globalStore . set ( curAtom , connStatus ) ;
} catch ( e ) {
console . log ( "connchange error" , e ) ;
2024-08-30 22:56:53 +02:00
}
2024-09-12 03:03:55 +02:00
} ,
2024-08-24 03:12:40 +02:00
} ) ;
}
2024-09-12 03:03:55 +02:00
function getConnStatusAtom ( conn : string ) : PrimitiveAtom < ConnStatus > {
2024-08-24 03:12:40 +02:00
let rtn = ConnStatusMap . get ( conn ) ;
if ( rtn == null ) {
2024-09-12 03:03:55 +02:00
if ( isBlank ( conn ) ) {
2024-09-05 09:21:08 +02:00
// create a fake "local" status atom that's always connected
const connStatus : ConnStatus = {
connection : conn ,
connected : true ,
error : null ,
status : "connected" ,
hasconnected : true ,
2024-09-06 20:13:55 +02:00
activeconnnum : 0 ,
2024-12-04 01:43:58 +01:00
wshenabled : false ,
2024-09-05 09:21:08 +02:00
} ;
2024-09-12 03:03:55 +02:00
rtn = atom ( connStatus ) ;
2024-09-05 09:21:08 +02:00
} else {
const connStatus : ConnStatus = {
connection : conn ,
connected : false ,
error : null ,
status : "disconnected" ,
hasconnected : false ,
2024-09-06 20:13:55 +02:00
activeconnnum : 0 ,
2024-12-04 01:43:58 +01:00
wshenabled : false ,
2024-09-05 09:21:08 +02:00
} ;
2024-09-12 03:03:55 +02:00
rtn = atom ( connStatus ) ;
2024-09-05 09:21:08 +02:00
}
2024-08-24 03:12:40 +02:00
ConnStatusMap . set ( conn , rtn ) ;
}
return rtn ;
}
2024-09-13 01:02:18 +02:00
function pushFlashError ( ferr : FlashErrorType ) {
if ( ferr . expiration == null ) {
ferr . expiration = Date . now ( ) + 5000 ;
}
ferr . id = crypto . randomUUID ( ) ;
globalStore . set ( atoms . flashErrors , ( prev ) = > {
return [ . . . prev , ferr ] ;
} ) ;
}
2024-11-16 06:26:16 +01:00
function addOrUpdateNotification ( notif : NotificationType ) {
globalStore . set ( atoms . notifications , ( prevNotifications ) = > {
// Remove any existing notification with the same ID
const notificationsWithoutThisId = prevNotifications . filter ( ( n ) = > n . id !== notif . id ) ;
// Add the new notification
return [ . . . notificationsWithoutThisId , notif ] ;
} ) ;
}
function pushNotification ( notif : NotificationType ) {
if ( ! notif . id && notif . persistent ) {
return ;
}
notif . id = notif . id ? ? crypto . randomUUID ( ) ;
addOrUpdateNotification ( notif ) ;
}
function removeNotificationById ( id : string ) {
globalStore . set ( atoms . notifications , ( prev ) = > {
return prev . filter ( ( notif ) = > notif . id !== id ) ;
} ) ;
}
2024-09-13 01:02:18 +02:00
function removeFlashError ( id : string ) {
globalStore . set ( atoms . flashErrors , ( prev ) = > {
return prev . filter ( ( ferr ) = > ferr . id !== id ) ;
} ) ;
}
2024-11-16 06:26:16 +01:00
function removeNotification ( id : string ) {
globalStore . set ( atoms . notifications , ( prev ) = > {
return prev . filter ( ( notif ) = > notif . id !== id ) ;
} ) ;
}
2024-12-04 05:08:06 +01:00
function createTab() {
getApi ( ) . createTab ( ) ;
}
function setActiveTab ( tabId : string ) {
2024-12-04 22:34:22 +01:00
// We use this hack to prevent a flicker in the tab bar when switching to a new tab. This class is unset in reinitWave in wave.ts. See tab.scss for where this class is used.
2024-12-04 05:08:06 +01:00
document . body . classList . add ( "nohover" ) ;
getApi ( ) . setActiveTab ( tabId ) ;
2024-10-17 23:34:02 +02:00
}
2024-06-12 23:18:03 +02:00
export {
atoms ,
2024-08-22 00:49:23 +02:00
counterInc ,
countersClear ,
countersPrint ,
2024-06-18 07:38:48 +02:00
createBlock ,
2024-10-17 23:34:02 +02:00
createTab ,
2024-06-18 07:38:48 +02:00
fetchWaveFile ,
2024-06-14 01:49:25 +02:00
getApi ,
2024-09-05 09:21:08 +02:00
getBlockComponentModel ,
2024-10-19 02:07:44 +02:00
getBlockMetaKeyAtom ,
2024-08-24 03:12:40 +02:00
getConnStatusAtom ,
2024-09-06 03:54:12 +02:00
getHostName ,
2024-07-09 00:04:48 +02:00
getObjectId ,
2024-11-21 19:44:16 +01:00
getOverrideConfigAtom ,
2024-10-07 23:08:57 +02:00
getSettingsKeyAtom ,
2024-08-28 03:49:49 +02:00
getUserName ,
2024-06-12 23:18:03 +02:00
globalStore ,
2024-07-19 22:44:32 +02:00
initGlobal ,
2024-09-12 03:03:55 +02:00
initGlobalWaveEventSubs ,
2024-08-01 22:46:06 +02:00
isDev ,
2024-08-24 03:12:40 +02:00
loadConnStatus ,
2024-08-07 23:27:16 +02:00
openLink ,
2024-08-15 03:40:41 +02:00
PLATFORM ,
2024-09-13 01:02:18 +02:00
pushFlashError ,
2024-11-16 06:26:16 +01:00
pushNotification ,
2024-09-03 01:48:10 +02:00
refocusNode ,
2024-09-05 09:21:08 +02:00
registerBlockComponentModel ,
2024-09-13 01:02:18 +02:00
removeFlashError ,
2024-11-16 06:26:16 +01:00
removeNotification ,
removeNotificationById ,
2024-12-04 05:08:06 +01:00
setActiveTab ,
2024-08-26 20:56:00 +02:00
setNodeFocus ,
2024-07-09 08:13:12 +02:00
setPlatform ,
2024-08-24 03:12:40 +02:00
subscribeToConnEvents ,
2024-09-05 09:21:08 +02:00
unregisterBlockComponentModel ,
2024-06-12 23:18:03 +02:00
useBlockAtom ,
useBlockCache ,
2024-08-22 00:49:23 +02:00
useBlockDataLoaded ,
2024-10-19 02:07:44 +02:00
useBlockMetaKeyAtom ,
useOverrideConfigAtom ,
2024-10-19 02:21:18 +02:00
useSettingsKeyAtom ,
2024-08-28 03:49:49 +02:00
useSettingsPrefixAtom ,
2024-08-15 03:40:41 +02:00
WOS ,
2024-06-12 23:18:03 +02:00
} ;