improve refresh and background script behavior
This commit is contained in:
@@ -26,7 +26,7 @@
|
||||
|
||||
export const staticOutputs: string[] = ['loading', 'result']
|
||||
|
||||
const { runnableComponents, worldStore, app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
const { worldStore, app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let labelValue: string
|
||||
let color: ButtonType.Color
|
||||
@@ -109,10 +109,6 @@
|
||||
} else {
|
||||
await runnableComponent?.runComponent()
|
||||
}
|
||||
|
||||
if (recomputeIds) {
|
||||
await Promise.all(recomputeIds.map((id) => $runnableComponents?.[id]?.()))
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -136,6 +132,7 @@
|
||||
|
||||
<RunnableWrapper
|
||||
flexWrap
|
||||
{recomputeIds}
|
||||
bind:runnableComponent
|
||||
{componentInput}
|
||||
{id}
|
||||
|
||||
@@ -23,8 +23,7 @@
|
||||
|
||||
export const staticOutputs: string[] = ['loading', 'result']
|
||||
|
||||
const { app, runnableComponents, worldStore, stateId } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
const { app, worldStore, stateId } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let labelValue: string = 'Default label'
|
||||
let color: ButtonType.Color
|
||||
@@ -62,6 +61,7 @@
|
||||
<InputValue {id} input={configuration.size} bind:value={size} />
|
||||
|
||||
<RunnableWrapper
|
||||
{recomputeIds}
|
||||
{render}
|
||||
bind:runnableComponent
|
||||
{componentInput}
|
||||
@@ -103,12 +103,6 @@
|
||||
}}
|
||||
on:click={() => {
|
||||
runnableComponent?.runComponent()
|
||||
|
||||
if (recomputeIds) {
|
||||
recomputeIds.forEach((id) => {
|
||||
$runnableComponents[id]?.()
|
||||
})
|
||||
}
|
||||
}}
|
||||
{size}
|
||||
{color}
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
}}
|
||||
>
|
||||
<RunnableWrapper
|
||||
{recomputeIds}
|
||||
{render}
|
||||
bind:runnableComponent
|
||||
{componentInput}
|
||||
@@ -128,12 +129,6 @@
|
||||
on:click={async () => {
|
||||
await runnableComponent?.runComponent()
|
||||
|
||||
if (recomputeIds) {
|
||||
recomputeIds.forEach((id) => {
|
||||
$runnableComponents[id]?.()
|
||||
})
|
||||
}
|
||||
|
||||
open = false
|
||||
}}
|
||||
size="xs"
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
export let name: string
|
||||
export let inlineScript: InlineScript | undefined
|
||||
export let fields: Record<string, StaticAppInput | ConnectedAppInput | RowAppInput | UserAppInput>
|
||||
export let autoRefresh: boolean = false
|
||||
|
||||
let result: any = undefined
|
||||
|
||||
@@ -22,6 +23,7 @@
|
||||
render={false}
|
||||
{id}
|
||||
{fields}
|
||||
{autoRefresh}
|
||||
bind:result
|
||||
runnable={{
|
||||
name,
|
||||
@@ -29,6 +31,7 @@
|
||||
type: 'runnableByName'
|
||||
}}
|
||||
wrapperClass="hidden"
|
||||
recomputable
|
||||
>
|
||||
<slot />
|
||||
</RunnableComponent>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
const { worldStore, state } = getContext<AppViewerContext>('AppViewerContext')
|
||||
const { worldStore, state, mode } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
$: stateId = $worldStore?.state
|
||||
|
||||
@@ -70,7 +70,8 @@
|
||||
input.expr,
|
||||
computeGlobalContext($worldStore, id, extraContext),
|
||||
true,
|
||||
$state
|
||||
$state,
|
||||
$mode == 'dnd'
|
||||
)
|
||||
error = ''
|
||||
return r
|
||||
@@ -87,7 +88,8 @@
|
||||
'`' + input.eval + '`',
|
||||
computeGlobalContext($worldStore, id, extraContext),
|
||||
true,
|
||||
$state
|
||||
$state,
|
||||
$mode == 'dnd'
|
||||
)
|
||||
error = ''
|
||||
return r
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import { faRefresh } from '@fortawesome/free-solid-svg-icons'
|
||||
import { RefreshCcw, RefreshCw } from 'lucide-svelte'
|
||||
import { RefreshCw } from 'lucide-svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
|
||||
|
||||
@@ -7,12 +7,10 @@
|
||||
import TestJobLoader from '$lib/components/TestJobLoader.svelte'
|
||||
import { AppService, type CompletedJob } from '$lib/gen'
|
||||
import { classNames, defaultIfEmptyString, emptySchema, sendUserToast } from '$lib/utils'
|
||||
import { Bug, Loader2 } from 'lucide-svelte'
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import { fade } from 'svelte/transition'
|
||||
import { Bug } from 'lucide-svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
import type { AppInputs, Runnable } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
import { computeGlobalContext, eval_like } from './eval'
|
||||
import InputValue from './InputValue.svelte'
|
||||
@@ -33,6 +31,8 @@
|
||||
export let gotoUrl: string | undefined = undefined
|
||||
export let gotoNewTab: boolean | undefined = undefined
|
||||
export let render: boolean
|
||||
export let recomputable: boolean = false
|
||||
export let recomputeIds: string[] = []
|
||||
|
||||
const {
|
||||
worldStore,
|
||||
@@ -48,14 +48,23 @@
|
||||
state
|
||||
} = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
onMount(() => {
|
||||
if (autoRefresh) {
|
||||
$: autoRefresh && handleAutorefresh()
|
||||
|
||||
if (recomputable) {
|
||||
$runnableComponents[id] = async () => {
|
||||
await executeComponent(true)
|
||||
}
|
||||
$runnableComponents = $runnableComponents
|
||||
}
|
||||
|
||||
function handleAutorefresh() {
|
||||
if (autoRefresh && $worldStore) {
|
||||
$runnableComponents[id] = async () => {
|
||||
await executeComponent(true)
|
||||
}
|
||||
executeComponent(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let args: Record<string, any> | undefined = undefined
|
||||
let testIsLoading = false
|
||||
@@ -83,17 +92,17 @@
|
||||
$: fields && (currentStaticValues = computeStaticValues())
|
||||
$: if (JSON.stringify(currentStaticValues) != JSON.stringify(lazyStaticValues)) {
|
||||
lazyStaticValues = currentStaticValues
|
||||
refreshIfAutoRefresh()
|
||||
}
|
||||
|
||||
function refreshIfAutoRefresh() {
|
||||
if (autoRefresh) {
|
||||
setDebouncedExecute()
|
||||
}
|
||||
}
|
||||
|
||||
$: fields && (lazyStaticValues = computeStaticValues())
|
||||
$: (runnableInputValues || extraQueryParams || args) &&
|
||||
autoRefresh &&
|
||||
testJobLoader &&
|
||||
setDebouncedExecute()
|
||||
$: (runnableInputValues || extraQueryParams || args) && testJobLoader && refreshIfAutoRefresh()
|
||||
|
||||
// Test job internal state
|
||||
let testJob: CompletedJob | undefined = undefined
|
||||
@@ -147,12 +156,13 @@
|
||||
runnable.inlineScript?.content,
|
||||
computeGlobalContext($worldStore, id, {}),
|
||||
false,
|
||||
$state
|
||||
$state,
|
||||
$mode == 'dnd'
|
||||
)
|
||||
setResult(r)
|
||||
$state = $state
|
||||
} catch (e) {
|
||||
sendUserToast('Error running frontend script: ' + e.message)
|
||||
sendUserToast('Error running frontend script: ' + e.message, true)
|
||||
}
|
||||
outputs?.loading?.set(false)
|
||||
return
|
||||
@@ -231,6 +241,7 @@
|
||||
|
||||
function setResult(res: any) {
|
||||
outputs.result?.set(res)
|
||||
|
||||
result = res
|
||||
|
||||
const previousJobId = Object.keys($errorByComponent).find(
|
||||
@@ -248,6 +259,10 @@
|
||||
goto(gotoUrl)
|
||||
}
|
||||
}
|
||||
|
||||
if (recomputeIds) {
|
||||
recomputeIds.map((id) => $runnableComponents?.[id]?.())
|
||||
}
|
||||
}
|
||||
$: result?.error && recordError(result.error)
|
||||
</script>
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
export let goto: string | undefined = undefined
|
||||
export let gotoNewTab: boolean | undefined = undefined
|
||||
export let render: boolean
|
||||
export let recomputeIds: string[] = []
|
||||
|
||||
const { staticExporter, noBackend } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
@@ -44,6 +45,7 @@
|
||||
<slot />
|
||||
{:else if componentInput.type === 'runnable' && isRunnableDefined()}
|
||||
<RunnableComponent
|
||||
{recomputeIds}
|
||||
gotoUrl={goto}
|
||||
{gotoNewTab}
|
||||
{flexWrap}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { goto } from '$app/navigation'
|
||||
import { sendUserToast } from '$lib/utils'
|
||||
import type { World } from '../../rx'
|
||||
|
||||
export function computeGlobalContext(
|
||||
@@ -45,9 +46,24 @@ function make_context_evaluator(
|
||||
return functor()
|
||||
}
|
||||
|
||||
export async function eval_like(text, context = {}, noReturn: boolean = true, state: any = {}) {
|
||||
export async function eval_like(
|
||||
text,
|
||||
context = {},
|
||||
noReturn: boolean,
|
||||
state: any,
|
||||
editor: boolean
|
||||
) {
|
||||
let evaluator = make_context_evaluator(text, context, noReturn)
|
||||
return await evaluator(context, state, async (x) => {
|
||||
await goto(x)
|
||||
return await evaluator(context, state, async (x, newTab) => {
|
||||
if (newTab || editor) {
|
||||
if (!newTab) {
|
||||
sendUserToast(
|
||||
'In editor mode, `goto` opens a new tab to prevent losing your work. To test the redirection , use the preview mode.'
|
||||
)
|
||||
}
|
||||
window.open(x, '_blank')
|
||||
} else {
|
||||
await goto(x)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -240,6 +240,7 @@
|
||||
inlineScript={script.inlineScript}
|
||||
name={script.name}
|
||||
fields={script.fields}
|
||||
autoRefresh={script.autoRefresh ?? false}
|
||||
bind:staticOutputs={$staticOutputs[`bg_${index}`]}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import Button from '../../common/button/Button.svelte'
|
||||
import type { AppViewerContext } from '../types'
|
||||
|
||||
const { runnableComponents } = getContext<AppViewerContext>('AppViewerContext')
|
||||
const { runnableComponents, app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
let loading: boolean = false
|
||||
let timeout: NodeJS.Timer | undefined = undefined
|
||||
let interval: number | undefined = undefined
|
||||
@@ -36,6 +36,12 @@
|
||||
loading = true
|
||||
Promise.all(
|
||||
Object.keys($runnableComponents).map((id) => {
|
||||
if (id.startsWith('bg_')) {
|
||||
let index = parseInt(id.split('_')[1])
|
||||
if (!$app.hiddenInlineScripts[index]?.autoRefresh) {
|
||||
return
|
||||
}
|
||||
}
|
||||
return $runnableComponents?.[id]?.()
|
||||
})
|
||||
).finally(() => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppViewerContext } from '../types'
|
||||
import GridPanel from './GridPanel.svelte'
|
||||
@@ -20,15 +21,27 @@
|
||||
|
||||
{#each $app?.hiddenInlineScripts ?? [] as script, index (script.name)}
|
||||
{#if $selectedComponent === `bg_${index}`}
|
||||
<PanelSection title={`Background script inputs`}>
|
||||
{#key $stateId}
|
||||
<InputsSpecsEditor
|
||||
id={`bg_${index}`}
|
||||
shouldCapitalize={false}
|
||||
bind:inputSpecs={script.fields}
|
||||
userInputEnabled={false}
|
||||
<div class="min-h-full flex flex-col divide-y">
|
||||
<PanelSection title={`Configuration`}>
|
||||
<Toggle
|
||||
bind:checked={script.autoRefresh}
|
||||
options={{ right: 'Run on start and app refresh' }}
|
||||
/>
|
||||
{/key}
|
||||
</PanelSection>
|
||||
</PanelSection>
|
||||
|
||||
{#if Object.keys(script.fields).length > 0}
|
||||
<PanelSection title={`Inputs`}>
|
||||
{#key $stateId}
|
||||
<InputsSpecsEditor
|
||||
id={`bg_${index}`}
|
||||
shouldCapitalize={false}
|
||||
bind:inputSpecs={script.fields}
|
||||
userInputEnabled={false}
|
||||
/>
|
||||
{/key}
|
||||
</PanelSection>
|
||||
{/if}
|
||||
<div class="grow shrink" />
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
</td>
|
||||
<td class="relative whitespace-nowrap px-4 py-2 ">
|
||||
<input
|
||||
class="windmillapp"
|
||||
type="checkbox"
|
||||
on:change={(event) => onChange(event, id)}
|
||||
checked={recomputeIds?.includes(id)}
|
||||
|
||||
@@ -100,6 +100,7 @@ export type App = {
|
||||
name: string
|
||||
inlineScript: InlineScript | undefined
|
||||
fields: Record<string, StaticAppInput | ConnectedAppInput | RowAppInput | UserAppInput>
|
||||
autoRefresh?: boolean
|
||||
}>
|
||||
css?: Record<'viewer' | 'grid' | AppComponent['type'], ComponentCustomCSS>
|
||||
subgrids?: Record<string, GridItem[]>
|
||||
|
||||
@@ -177,7 +177,7 @@ export function buildExtraLib(
|
||||
|
||||
return `${cs}
|
||||
${hasRows ? 'declare const row: Record<string, any>;' : ''}
|
||||
${goto ? 'declare const goto: async (path: string) => void)' : ''}
|
||||
${goto ? 'declare const goto: async (path: string, newTab?: boolean) => void)' : ''}
|
||||
declare const state: ${JSON.stringify(state)};
|
||||
`
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
$workspaceStore = $page.url.searchParams.get('workspace_id')!
|
||||
}
|
||||
|
||||
$: if ($workspaceStore) {
|
||||
$: if ($workspaceStore && $page.params.path) {
|
||||
loadApp()
|
||||
}
|
||||
|
||||
@@ -32,34 +32,39 @@
|
||||
</script>
|
||||
|
||||
{#if app}
|
||||
<div
|
||||
class={twMerge('min-h-screen h-full w-full', app?.value.css?.['app']?.['viewer']?.class)}
|
||||
style={app?.value.css?.['app']?.['viewer']?.style}
|
||||
>
|
||||
<AppPreview
|
||||
context={{
|
||||
email: $userStore?.email,
|
||||
username: $userStore?.username,
|
||||
query: Object.fromEntries($page.url.searchParams.entries()),
|
||||
hash: $page.url.hash
|
||||
}}
|
||||
workspace={$workspaceStore ?? ''}
|
||||
summary={app.summary}
|
||||
app={app.value}
|
||||
appPath={app.path}
|
||||
{breakpoint}
|
||||
policy={app.policy}
|
||||
isEditor={false}
|
||||
noBackend={false}
|
||||
/>
|
||||
{#if can_write}
|
||||
<div class="absolute bottom-4 z-20 right-4">
|
||||
<Button size="sm" startIcon={{ icon: faPen }} variant="border" href="/apps/edit/{app.path}"
|
||||
>Edit</Button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#key app}
|
||||
<div
|
||||
class={twMerge('min-h-screen h-full w-full', app?.value.css?.['app']?.['viewer']?.class)}
|
||||
style={app?.value.css?.['app']?.['viewer']?.style}
|
||||
>
|
||||
<AppPreview
|
||||
context={{
|
||||
email: $userStore?.email,
|
||||
username: $userStore?.username,
|
||||
query: Object.fromEntries($page.url.searchParams.entries()),
|
||||
hash: $page.url.hash
|
||||
}}
|
||||
workspace={$workspaceStore ?? ''}
|
||||
summary={app.summary}
|
||||
app={app.value}
|
||||
appPath={app.path}
|
||||
{breakpoint}
|
||||
policy={app.policy}
|
||||
isEditor={false}
|
||||
noBackend={false}
|
||||
/>
|
||||
{#if can_write}
|
||||
<div class="absolute bottom-4 z-20 right-4">
|
||||
<Button
|
||||
size="sm"
|
||||
startIcon={{ icon: faPen }}
|
||||
variant="border"
|
||||
href="/apps/edit/{app.path}">Edit</Button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/key}
|
||||
{:else}
|
||||
<Skeleton layout={[10]} />
|
||||
{/if}
|
||||
|
||||
@@ -71,27 +71,29 @@
|
||||
</Alert></div
|
||||
>
|
||||
{:else if app}
|
||||
<div
|
||||
class={twMerge('min-h-screen h-full w-full', app?.value.css?.['app']?.['viewer']?.class)}
|
||||
style={app?.value.css?.['app']?.['viewer']?.style}
|
||||
>
|
||||
<AppPreview
|
||||
noBackend={false}
|
||||
context={{
|
||||
email: $userStore?.email,
|
||||
username: $userStore?.username,
|
||||
query: Object.fromEntries($page.url.searchParams.entries()),
|
||||
hash: $page.url.hash
|
||||
}}
|
||||
workspace={$page.params.workspace}
|
||||
summary={app.summary}
|
||||
app={app.value}
|
||||
appPath={app.path}
|
||||
{breakpoint}
|
||||
policy={app.policy}
|
||||
isEditor={false}
|
||||
/>
|
||||
</div>
|
||||
{#key app}
|
||||
<div
|
||||
class={twMerge('min-h-screen h-full w-full', app?.value.css?.['app']?.['viewer']?.class)}
|
||||
style={app?.value.css?.['app']?.['viewer']?.style}
|
||||
>
|
||||
<AppPreview
|
||||
noBackend={false}
|
||||
context={{
|
||||
email: $userStore?.email,
|
||||
username: $userStore?.username,
|
||||
query: Object.fromEntries($page.url.searchParams.entries()),
|
||||
hash: $page.url.hash
|
||||
}}
|
||||
workspace={$page.params.workspace}
|
||||
summary={app.summary}
|
||||
app={app.value}
|
||||
appPath={app.path}
|
||||
{breakpoint}
|
||||
policy={app.policy}
|
||||
isEditor={false}
|
||||
/>
|
||||
</div>
|
||||
{/key}
|
||||
{:else}
|
||||
<Skeleton layout={[[4], 0.5, [50]]} />
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user