-
Notifications
You must be signed in to change notification settings - Fork 84
Expand file tree
/
Copy pathreactMain.js
More file actions
556 lines (514 loc) · 28.2 KB
/
reactMain.js
File metadata and controls
556 lines (514 loc) · 28.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
/* eslint-disable max-len */
// @flow
//-----------------------------------------------------------------------------
// Dashboard plugin main file (for React v2.0.0+)
// Last updated 2026-01-09 for v2.4.0.b14 by @jgclark
//-----------------------------------------------------------------------------
import pluginJson from '../plugin.json'
import { allSectionDetails, WEBVIEW_WINDOW_ID } from './constants'
import { updateDoneCountsFromChangedNotes } from './countDoneTasks'
import { getDashboardSettings, getDashboardSettingsDefaults, getLogSettings, getNotePlanSettings, getListOfEnabledSections, setPluginData } from './dashboardHelpers'
import { dashboardFilterDefs, dashboardSettingDefs } from './dashboardSettings'
import { getAllSectionsData } from './dataGeneration'
import { getPerspectiveSettings, getActivePerspectiveDef, switchToPerspective } from './perspectiveHelpers'
import { incrementallyRefreshSomeSections } from './refreshClickHandlers'
import { onMessageFromHTMLView } from './routeRequestsFromReact'
import { generateTagMentionCache, isTagMentionCacheGenerationScheduled } from './tagMentionCache'
import type { TDashboardSettings, TPerspectiveDef, TPluginData, TPerspectiveSettings } from './types'
import { clo, clof, JSP, logDebug, logInfo, logError, logTimer, logWarn } from '@helpers/dev'
import { createPrettyRunPluginLink, createRunPluginCallbackUrl } from '@helpers/general'
import { getGlobalSharedData, type HtmlWindowOptions } from '@helpers/HTMLView'
import { getSettings, saveSettings } from '@helpers/NPConfiguration'
import { generateCSSFromTheme } from '@helpers/NPThemeToCSS'
import { usersVersionHas } from '@helpers/NPVersions'
import { chooseOption, showMessage } from '@helpers/userInput'
//------------------------------------------------------------------------------
// Constants
const pluginID = 'jgclark.Dashboard'
const RESOURCE_LINKS_FOR_HEADER = `
<!-- <link rel="stylesheet" href="../np.Shared/css.w3.css"> -->
<!-- Load in fontawesome assets from np.Shared (licensed for NotePlan) -->
<link href="../np.Shared/fontawesome.css" rel="stylesheet">
<link href="../np.Shared/regular.min.flat4NP.css" rel="stylesheet">
<link href="../np.Shared/solid.min.flat4NP.css" rel="stylesheet">
<link href="../np.Shared/light.min.flat4NP.css" rel="stylesheet">
<link href="../np.Shared/duotone.min.flat4NP.css" rel="stylesheet">
`
//------------------------------------------------------------------------------
// Types
export type PassedData = {
pluginData: TPluginData /* Your plugin's data to pass on first launch (or edited later) */,
returnPluginCommand: { id: string, command: string } /* plugin jsFunction that will receive comms back from the React window */,
componentPath: string /* the path to the rolled up webview bundle. should be ../pluginID/react.c.WebView.bundle.* */,
startTime?: Date /* used for timing/debugging */,
title?: string /* React Window Title */,
debug: boolean /* set based on ENV_MODE above */,
ENV_MODE?: 'development' | 'production',
passThroughVars?: any /* any data you want to pass through to the React Window */,
windowID?: string,
initialBanner?: {
// TODO(later): remove this, as it was only used for debugging/testing the warning banner
msg: string,
color: string,
border: string,
icon: string,
},
}
// ------------------------------------------------------------
/**
* Show dashboard using Demo dashboard, and last used perspective.
*/
export async function showDemoDashboard(): Promise<void> {
await showDashboardReact('full', '', true)
}
/**
* x-callback entry point to change a single setting.
* (Note: see also setSettings which does many at the same time.)
* FIXME: doesn't work for show*Sections?
* @param {string} key
* @param {string} value
* @example noteplan://x-callback-url/runPlugin?pluginID=jgclark.Dashboard&command=setSetting&arg0=rescheduleNotMove&arg1=true
* @example noteplan://x-callback-url/runPlugin?pluginID=jgclark.Dashboard&command=setSetting&arg0=ignoreItemsWithTerms&arg1=#waiting
*/
export async function setSetting(key: string, value: string): Promise<void> {
try {
logDebug('setSetting', `Request to set: '${key}' -> '${value}'`)
const dashboardSettings = (await getDashboardSettings()) || {}
// clo(dashboardSettings, 'dashboardSettings:')
const allSettings = [...dashboardFilterDefs, ...dashboardSettingDefs].filter((k) => k.label && k.key)
const allKeys = allSettings.map((s) => s.key)
logDebug('setSetting', `Existing setting keys: ${String(allKeys)}`)
if (allKeys.includes(key)) {
const thisSettingDetail = allSettings.find((s) => s.key === key) || {}
const setTo = thisSettingDetail.type === 'switch' ? value === 'true' : value
// $FlowFixMe[prop-missing]
dashboardSettings[key] = setTo
// logDebug('setSetting', `Set ${key} to ${String(setTo)} in dashboardSettings (type: ${typeof setTo} / ${thisSettingType})`)
// TEST: use helper to save settings from now on
// DataStore.settings = { ...await getSettings('jgclark.Dashboard'), dashboardSettings: JSON.stringify(dashboardSettings) }
const res = await saveSettings(pluginID, { ...(await getSettings('jgclark.Dashboard')), dashboardSettings: dashboardSettings })
if (!res) {
throw new Error(`saveSettings failed for setting '${key}:${value}'`)
}
await showDashboardReact('full', '', false)
} else {
throw new Error(`Key '${key}' not found in dashboardSettings. Available keys: [${allKeys.join(', ')}]`)
}
} catch (error) {
logError('setSetting', error.message)
}
}
/**
* x-callback entry point to change multiple settings in one go.
* @param {string} `key=value` pairs separated by ;
* @example noteplan://x-callback-url/runPlugin?pluginID=jgclark.Dashboard&command=setSetting&arg0=rescheduleNotMove=true;ignoreItemsWithTerms=#waiting
*/
export async function setSettings(paramsIn: string): Promise<void> {
try {
const dashboardSettings = (await getDashboardSettings()) || {}
const allSettings = [...dashboardFilterDefs, ...dashboardSettingDefs].filter((k) => k.label && k.key)
const allKeys = allSettings.map((s) => s.key)
const params = paramsIn.split(';')
logDebug('setSettings', `Given ${params.length} key=value pairs to set:`)
const i = 0
for (const param of params) {
const [key, value] = param.split('=')
logDebug('setSettings', `- ${String(i)}: setting '${key}' -> '${value}'`)
if (allKeys.includes(key)) {
const thisSettingDetail = allSettings.find((s) => s.key === key) || {}
const setTo = thisSettingDetail.type === 'switch' ? value === 'true' : value
// $FlowFixMe[prop-missing]
dashboardSettings[key] = setTo
logDebug('setSettings', ` - set ${key} to ${String(setTo)} in dashboardSettings (type: ${typeof setTo})`)
} else {
throw new Error(`Key '${key}' not found in dashboardSettings. Available keys: [${allKeys.join(', ')}]`)
}
}
logDebug('setSettings', `Calling DataStore.settings, then showDashboardReact()`)
// TEST: use helper to save settings from now on
// DataStore.settings = { ...await getSettings('jgclark.Dashboard'), dashboardSettings: JSON.stringify(dashboardSettings) }
const res = await saveSettings(pluginID, { ...(await getSettings('jgclark.Dashboard')), dashboardSettings: JSON.stringify(dashboardSettings) })
if (!res) {
throw new Error(`saveSettings failed for params: '${paramsIn}'`)
}
await showDashboardReact('full', '', false)
} catch (error) {
logError('setSettings', error.message)
}
}
/**
* Make a callback with all the current settings in it, and
*/
export async function makeSettingsAsCallback(): Promise<void> {
try {
const dashboardSettings = (await getDashboardSettings()) || {}
const params = Object.keys(dashboardSettings)
.map((k) => `${k}=${String(dashboardSettings[k])}`)
.join(';')
// then give user the choice of whether they want a raw URL or a pretty link.
const options = [
{ label: 'raw URL', value: 'raw' },
{ label: 'pretty link', value: 'link' },
]
const result = await chooseOption('Settings as URL or Link?', options, 'raw URL')
let output = ''
// let clipboardType = ''
// then make the URL, using helpers to deal with encodings.
switch (result) {
case 'raw':
output = createRunPluginCallbackUrl('jgclark.Dashboard', 'setSettings', params)
// clipboardType = 'public.url'
break
case 'link':
output = createPrettyRunPluginLink('Set all Dashboard settings to your current ones', 'jgclark.Dashboard', 'setSettings', params)
// clipboardType = 'public.utf8-plain-text'
break
default:
return
}
logDebug('makeSettingsAsCallback', `${result} output: '${output}'`)
// now copy to Clipboard and tell the user
// This does not work for JGC:
// Clipboard.setStringForType(output, clipboardType)
// But this simpler version does:
Clipboard.string = output
await showMessage('Settings as URL or Link copied to Clipboard', 'OK', 'Dashboard', false)
} catch (error) {
logError('makeSettingsAsCallback', error.message)
}
}
/**
* x-callback entry point to show (or switch to) Dashboard with a named Perspective.
* @param {string} name (optional; if not given then will use last-used Perspective)
* @example noteplan://x-callback-url/runPlugin?pluginID=jgclark.Dashboard&command=showPerspective&arg0=Work
*/
export async function showPerspective(name: string = ''): Promise<void> {
try {
logDebug('showPerspective', `Request to show Dashboard with Perspective '${name}'`)
await showDashboardReact('full', name, false)
} catch (error) {
logError('showPerspective', error.message)
}
}
/**
* x-callback entry point to show (or switch to) Dashboard with a CSV list of specific Sections to show.
* @param {string} sectionCodeList comma-separated list of sectionCodes (e.g. 'DT' for Today)
* @example noteplan://x-callback-url/runPlugin?pluginID=jgclark.Dashboard&command=showSections&arg0=DT,DO,@home
*/
export async function showSections(sectionCodeList: string = ''): Promise<void> {
try {
logDebug('showSections', `Request to show Dashboard with Sections '${sectionCodeList}'`)
await showDashboardReact(sectionCodeList, '', false)
} catch (error) {
logError('showSections', error.message)
}
}
/**
* Set the dashboard settings to show only the sections in the CSV string.
* (if user calls this from xcallback, it will override the current settings, showing only the sections in the CSV string)
* This is less necessary now that we have the ability to load a specific perspective.
* But it's still useful for xcallback calls to focus on a specific section or sections.
* @param {string} limitToSections e.g. "TD,TY,#work"
*/
async function updateSectionFlagsToShowOnly(limitToSections: string): Promise<void> {
try {
if (!limitToSections) return
logDebug('updateSectionFlagsToShowOnly', `Request to show only sections '${limitToSections}'`)
const dashboardSettings: TDashboardSettings = (await getDashboardSettings()) || {}
// set everything off to begin with
const keys = Object.keys(dashboardSettings).filter((key) => key.startsWith('show') && key.endsWith('Section'))
allSectionDetails.forEach((section) => {
const key = section.showSettingName
// $FlowIgnore[prop-missing]
if (key) dashboardSettings[key] = false
})
// also turn off the specific tag sections (e.g. "showTagSection_@home")
// $FlowIgnore[incompatible-type]
keys.forEach((key) => (dashboardSettings[key] = false))
const sectionsToShow = limitToSections.split(',')
sectionsToShow.forEach((sectionCode) => {
const showSectionKey = allSectionDetails.find((section) => section.sectionCode === sectionCode)?.showSettingName
if (showSectionKey) {
// $FlowIgnore
dashboardSettings[showSectionKey] = true
} else {
if (sectionCode.startsWith('@') || sectionCode.startsWith('#')) {
// $FlowIgnore
dashboardSettings[`showTagSection_${sectionCode}`] = true
} else {
logWarn(pluginJson, `updateSectionFlagsToShowOnly: sectionCode '${sectionCode}' not found in allSectionDetails. Continuing with others.`)
}
}
})
// TEST: use helper to save settings from now on
// DataStore.settings = { ...await getSettings('jgclark.Dashboard'), dashboardSettings: JSON.stringify(dashboardSettings) }
const res = await saveSettings(pluginID, { ...(await getSettings('jgclark.Dashboard')), dashboardSettings: JSON.stringify(dashboardSettings) })
if (!res) {
throw new Error(`saveSettings failed for sections '${limitToSections}'`)
}
} catch (error) {
logError('updateSectionFlagsToShowOnly', error.message)
}
}
/**
* Plugin Entry Point for "Show Dashboard"
* @author @dwertheimer
* @param {string?} callMode (default: 'full') 'full' (i.e. by user call) | 'trigger' (by trigger: don't steal focus) | CSV of specific sections to load (e.g. from xcallback)
* @param {string?} perspectiveName (default: ''): perspective to start it with. If none given (empty string) then open in the last used one.
* @param {boolean?} useDemoData? (default: false)
*/
export async function showDashboardReact(callMode: string = 'full', perspectiveName: string = '', useDemoData: boolean = false): Promise<void> {
try {
logInfo(pluginJson, `showDashboardReact starting up (mode '${callMode}') with perpsectiveName '${perspectiveName}' ${useDemoData ? 'in DEMO MODE' : 'using LIVE data'}`)
// clo(DataStore.settings, `showDashboardReact: DataStore.settings=`)
const startTime = new Date()
// If callMode is a CSV of specific wanted sections, then override section flags for them
// (e.g. from xcallback)
if (callMode !== 'trigger' && callMode !== 'full') await updateSectionFlagsToShowOnly(callMode)
// Get settings
const config = await getDashboardSettings() // pulls the JSON stringified dashboardSettings and parses it into object
// clo(config, `showDashboardReact: keys:${Object.keys(config).length} config=`)
const logSettings = await getLogSettings()
const settings = await getSettings(pluginID)
// get initial data to pass to the React Window
const data = await getInitialDataForReactWindow(perspectiveName, useDemoData)
// logDebug('showDashboardReact', `lastFullRefresh = ${String(data?.pluginData?.lastFullRefresh) || 'not set yet'}`)
const preferredWindowType = settings?.preferredWindowType ?? 'New Window'
const platform = NotePlan.environment.platform
logDebug('showDashboardReact', `preferredWindowType = ${preferredWindowType} / platform = ${platform}`)
const windowOptions: HtmlWindowOptions = {
windowTitle: data?.title || 'Dashboard',
customId: WEBVIEW_WINDOW_ID,
makeModal: false,
savedFilename: `../../${pluginJson['plugin.id']}/dashboard-react.html` /* for saving a debug version of the html file */,
shouldFocus: callMode !== 'trigger' /* focus window (unless called by a trigger) */,
reuseUsersWindowRect: true,
headerTags: `${RESOURCE_LINKS_FOR_HEADER}\n<meta name="startTime" content="${String(Date.now())}">`,
generalCSSIn: generateCSSFromTheme(config.dashboardTheme), // either use dashboard-specific theme name, or get general CSS set automatically from current theme
specificCSS: '', // set in separate CSS file referenced in header
preBodyScript: `
<script type="text/javascript" >
// Set DataStore.settings so default logDebug etc. logging works in React
// This setting comes from ${pluginJson['plugin.id']}
let DataStore = { settings: {_logLevel: "${DataStore?.settings?._logLevel}" } };
</script>`,
postBodyScript: ``,
paddingWidth: platform === 'iPadOS' ? 32 : platform === 'iOS' ? 0 : 0,
paddingHeight: platform === 'iPadOS' ? 32 : platform === 'iOS' ? 0 : 0,
// If we should open in main/split view, or the default new window
showInMainWindow: preferredWindowType !== 'New Window',
splitView: preferredWindowType === 'Split View',
// If we are opening in main/split view, then set the icon details
// TODO: later, move this to plugin.json file
icon: 'fa-gauge-high',
// icon: 'fa-duotone fa-gauge-high', // TODO(Eduard): support other icon sets
// icon: 'fa-duotone fa-grid-round-2', // TODO: this icon is not available in our old build
iconColor: 'red-600',
autoTopPadding: true,
showReloadButton: true,
reloadPluginID: pluginID,
reloadCommandName: "refreshDashboard",
}
logTimer('showDashboardReact', startTime, `Finished getting initial data. Now will call React:`)
logDebug(pluginJson, `showDashboardReact invoking window. showDashboardReact is finished. It's all React from this point forward...\n`)
await DataStore.invokePluginCommandByName('openReactWindow', 'np.Shared', [data, windowOptions])
} catch (error) {
logError('showDashboardReact', JSP(error))
}
}
/**
* This is called from Dashboard.jsx when the React Window has initialised, and is ready to start receiving Sections.
* It kicks off the incremental generation of the Sections.
* @returns {Promise<void>}
*/
export async function reactWindowInitialisedSoStartGeneratingData(): Promise<void> {
try {
logDebug('reactWindowInitialisedSoStartGeneratingData', `--> React Window reported back to plugin that it has loaded <--`)
const startTime = new Date()
const config = await getDashboardSettings()
const logSettings = await getLogSettings()
const enabledSections = getListOfEnabledSections(config)
// Start generating data for the enabled sections
if (!config.FFlag_ForceInitialLoadForBrowserDebugging) {
await incrementallyRefreshSomeSections({ sectionCodes: enabledSections, actionType: 'incrementallyRefreshSomeSections' }, false, true)
} else {
// If force initial load is enabled, we still need to set firstRun to false
const reactWindowData = await getGlobalSharedData(WEBVIEW_WINDOW_ID)
if (reactWindowData?.pluginData) {
await setPluginData({ firstRun: false }, 'Setting firstRun to false after force initial load')
}
}
logTimer('reactWindowInitialisedSoStartGeneratingData', startTime, `----- END OF GENERATION ------`)
logInfo('reactWindowInitialisedSoStartGeneratingData', `----- END OF GENERATION ------`)
// ---------------------------------------------------------------
// Now is the time to do any other background processing after the initial display is done
// ---------------------------------------------------------------
// Rebuild the tag mention cache. (Ideally this would be triggered by NotePlan once a day, but for now we will do it here.)
if (isTagMentionCacheGenerationScheduled()) {
logInfo('reactWindowInitialisedSoStartGeneratingData', `- now generating tag mention cache`)
await generateTagMentionCache()
// Now that the cache is generated, we want to re-generate any enabled tag section(s), just in case
if (enabledSections.length > 0 && enabledSections.includes('TAG')) {
logInfo('reactWindowInitialisedSoStartGeneratingData', `- now re-generating tag section(s)`)
await incrementallyRefreshSomeSections({ sectionCodes: ['TAG'], actionType: 'incrementallyRefreshSomeSections' }, false, true)
}
}
} catch (error) {
logError('reactWindowInitialisedSoStartGeneratingData', error.message)
}
}
/**
* Because perspectiveSettings may need to be created on first run, we need to get the dashboardSettings from the perspectiveSettings
* @param {Array<TPerspectiveDef>} perspectiveSettings
* @returns {TDashboardSettings}
*/
async function getDashboardSettingsFromPerspective(perspectiveSettings: TPerspectiveSettings): Promise<TDashboardSettings> {
try {
const activeDef = getActivePerspectiveDef(perspectiveSettings)
if (!activeDef) throw new Error(`getDashboardSettingsFromPerspective: getActivePerspectiveDef failed`)
const prevDashboardSettings = await getDashboardSettings()
if (!prevDashboardSettings) throw new Error(`getDashboardSettingsFromPerspective: getDashboardSettings failed`)
// Get defaults to ensure all section show settings are included
// $FlowIgnore[incompatible-call] - getDashboardSettingsDefaults is exported from dashboardHelpers
const defaults = getDashboardSettingsDefaults()
// apply the new perspective's settings to the main dashboard settings
// Merge order: defaults -> prevDashboardSettings -> perspective settings
// This ensures that if a perspective doesn't have a section show setting (like showProjectActiveSection),
// it will use the default value (true for most sections) from defaults or prevDashboardSettings
const newDashboardSettings = {
...defaults,
...prevDashboardSettings,
...(activeDef.dashboardSettings || {}),
}
// use our more reliable helper to save settings
const res = await saveSettings(pluginID, { ...(await getSettings('jgclark.Dashboard')), dashboardSettings: newDashboardSettings })
if (!res) {
throw new Error(`saveSettings failed`)
}
return newDashboardSettings
} catch (error) {
logError('getDashboardSettingsFromPerspective', error.message)
// $FlowFixMe[prop-missing]
return {}
}
}
/**
* Gathers key data for the React Window, including the callback function that is used for comms back to the plugin.
* @param {string?} perspectiveName - the name of the Perspective to use, or blank to mean use the current one
* @param {boolean?} useDemoData - whether to use demo data
* @returns {PassedData} the React Data Window object
*/
export async function getInitialDataForReactWindow(perspectiveName: string = '', useDemoData: boolean = false): Promise<PassedData> {
try {
logDebug('getInitialDataForReactWindow', `>>>>> Starting`)
const startTime = new Date()
let perspectiveSettings = await getPerspectiveSettings()
// If a perspective is specified, then update the setting to point to it before opening the React Window
let dashboardSettings: TDashboardSettings = await getDashboardSettings()
if (perspectiveName) {
logDebug('getInitialDataForReactWindow', `will use perspective '${perspectiveName}'`)
perspectiveSettings = (await switchToPerspective(perspectiveName, perspectiveSettings)) || perspectiveSettings
dashboardSettings = await getDashboardSettingsFromPerspective(perspectiveSettings)
}
// Ensure all keys (including show*Section from allSectionDetails) have a value so the UI switches get correct state
const defaults = getDashboardSettingsDefaults()
dashboardSettings = { ...defaults, ...dashboardSettings }
// clo(dashboardSettings, `getInitialDataForReactWindow: dashboardSettings=`)
// get whatever pluginData you want the React window to start with and include it in the object below. This all gets passed to the React window
const pluginData = await getPluginData(dashboardSettings, perspectiveSettings, useDemoData) // Note: the only time this is called.
logDebug('getInitialDataForReactWindow', `lastFullRefresh = ${String(pluginData.lastFullRefresh)}`)
const ENV_MODE = 'development' /* 'development' helps during development. set to 'production' when ready to release */
const dataToPass: PassedData = {
pluginData,
returnPluginCommand: { id: pluginJson['plugin.id'], command: 'onMessageFromHTMLView' },
componentPath: `../${pluginJson['plugin.id']}/react.c.WebView.bundle.${ENV_MODE === 'development' ? 'dev' : 'min'}.js`,
debug: false, // ENV_MODE === 'development' ? true : false, // certain logging on/off, including the pluginData display at the bottom of the screen
title: useDemoData ? 'Dashboard (Demo Data)' : 'Dashboard',
ENV_MODE,
startTime,
windowID: WEBVIEW_WINDOW_ID,
// For testing the warning banner outside NP
// initialBanner: {
// msg: 'jgclark testing the warning banner',
// color: 'w3-pale-red', // optional
// border: 'w3-border-red', // optional
// icon: 'fa-regular fa-circle-exclamation', // optional
// },
}
logTimer('getInitialDataForReactWindow', startTime, `<<<<< Finished`)
logDebug('getInitialDataForReactWindow', `<<<<< Finished`)
return dataToPass
} catch (error) {
logError(pluginJson, error.message)
// $FlowFixMe[prop-missing]
return {}
}
}
/**
* Note: This comes from the React Template, but is no longer used, so commenting out.
* Update the data in the React Window (and cause it to re-draw as necessary with the new data)
* This is likely most relevant when a trigger has been sent from a NotePlan window, but could be used anytime a plugin wants to update the data in the React Window
* This is exactly the same as onMessageFromHTMLView, but named updateReactWindowData to clarify that the plugin is updating the data in the React Window
* rather than a user interaction having triggered it (the result is the same)
* See discussion at https://discord.com/channels/@me/863719873175093259/1229524619615010856
* @param {string} actionType - the reducer-type action to be dispatched -- see onMessageFromHTMLView above
* @param {any} data - any data that the router (specified in onMessageFromHTMLView) needs -- may be nothing
* @returns {Promise<any>} - does not return anything important
*/
export async function updateReactWindowData(actionType: string, data: any = null): Promise<any> {
try {
await onMessageFromHTMLView(actionType, data)
} catch (error) {
logError(`updateReactWindowData`, `Error "${error.message}" for action '${actionType}'`)
}
}
/**
* Router function that receives requests from the React Window and routes them to the appropriate function
* Moved to routeRequestsFromReact.js to follow the standard pattern
* @author @dwertheimer
* @deprecated Use routeRequestsFromReact.js instead
*/
export { onMessageFromHTMLView } from './routeRequestsFromReact'
/**
* Gather data you want passed to the React Window (e.g. what you you will use to display).
* You will likely use this function to pull together your starting window data.
* Must return an object, with any number of properties, however you cannot use the following reserved properties:
* pluginData, title, debug, ENV_MODE, returnPluginCommand, componentPath, passThroughVars, startTime
* @returns {[string]: mixed} - the data that your React Window will start with
*/
export async function getPluginData(dashboardSettings: TDashboardSettings, perspectiveSettings: Array<TPerspectiveDef>, useDemoData: boolean = false): Promise<TPluginData> {
logDebug('getPluginData', `Starting ${useDemoData ? 'with DEMO DATA!' : ''}`)
// Important Note: If we need to force load everything, it's easy.
// But if we don't then 2 things are needed:
// - the getSomeSectionsData() for just the Today section(s)
// - then once the HTML Window is available, Dialog.jsx realises that <= 2 sections, and kicks off incrementallyRefreshSomeSections to generate the others
const sections = dashboardSettings.FFlag_ForceInitialLoadForBrowserDebugging === true ? await getAllSectionsData(useDemoData, true, true) : []
const NPSettings = getNotePlanSettings()
const pluginData: TPluginData = {
sections: sections,
lastFullRefresh: new Date(),
dashboardSettings: dashboardSettings,
perspectiveSettings: perspectiveSettings,
notePlanSettings: NPSettings,
logSettings: await getLogSettings(),
demoMode: useDemoData,
platform: NotePlan.environment.platform, // used in window/dialog management
themeName: dashboardSettings.dashboardTheme ? dashboardSettings.dashboardTheme : Editor.currentTheme?.name || '<could not get theme>',
version: pluginJson['plugin.version'],
pushFromServer: {
dashboardSettings: true,
perspectiveSettings: true,
},
totalDoneCount: 0,
firstRun: true,
currentMaxPriorityFromAllVisibleSections: 0,
mainWindowModeSupported: NotePlan.environment.platform === 'macOS' ? usersVersionHas('showInMainWindow') : usersVersionHas('showInMainWindowOniOS'),
}
logDebug('getPluginData', `After forming initial pluginData, firstRun = false`)
// Calculate all done task counts (if the appropriate setting is on)
if (NPSettings.doneDatesAvailable) {
const totalDoneCount = await updateDoneCountsFromChangedNotes('end of getPluginData', dashboardSettings.FFlag_ShowSectionTimings === true)
pluginData.totalDoneCount = totalDoneCount
}
return pluginData
}