251 lines
8.5 KiB
Svelte
251 lines
8.5 KiB
Svelte
<script lang="ts">
|
|
import { createEventDispatcher } from 'svelte'
|
|
// import { getProfile } from './Profiles.svelte'
|
|
import { cleanSettingValue, setChatSettingValue } from './Storage.svelte'
|
|
import type { Chat, ChatSetting, ChatSettings, ControlAction, FieldControl, SettingPrompt } from './Types.svelte'
|
|
import { autoGrowInputOnEvent, errorNotice, valueOf } from './Util.svelte'
|
|
// import { replace } from 'svelte-spa-router'
|
|
import Fa from 'svelte-fa/src/fa.svelte'
|
|
import { openModal } from 'svelte-modals'
|
|
import PromptConfirm from './PromptConfirm.svelte'
|
|
import { afterUpdate, onMount } from 'svelte'
|
|
|
|
export let setting:ChatSetting
|
|
export let chatSettings:ChatSettings
|
|
export let chat:Chat
|
|
export let chatDefaults:Record<string, any>
|
|
export let originalProfile:String
|
|
export let rkey:number = 0
|
|
|
|
|
|
let fieldControls:ControlAction[]
|
|
|
|
const chatId = chat.id
|
|
let show = false
|
|
|
|
let header = valueOf(chatId, setting.header)
|
|
let headerClass = valueOf(chatId, setting.headerClass)
|
|
let placeholder = valueOf(chatId, setting.placeholder)
|
|
|
|
const buildFieldControls = () => {
|
|
fieldControls = (setting.fieldControls || [] as FieldControl[]).map(fc => {
|
|
return fc.getAction(chatId, setting, chatSettings[setting.key])
|
|
})
|
|
}
|
|
|
|
buildFieldControls()
|
|
|
|
onMount(() => {
|
|
show = (typeof setting.hide !== 'function') || !setting.hide(chatId, setting)
|
|
buildFieldControls()
|
|
})
|
|
|
|
afterUpdate(() => {
|
|
show = (typeof setting.hide !== 'function') || !setting.hide(chatId, setting)
|
|
header = valueOf(chatId, setting.header)
|
|
headerClass = valueOf(chatId, setting.headerClass)
|
|
placeholder = valueOf(chatId, setting.placeholder)
|
|
buildFieldControls()
|
|
})
|
|
|
|
|
|
if (originalProfile) {
|
|
// eventually...
|
|
}
|
|
|
|
const dispatch = createEventDispatcher()
|
|
|
|
const refreshSettings = () => {
|
|
dispatch('refresh')
|
|
}
|
|
|
|
const settingChecks:Record<string, SettingPrompt[]> = {
|
|
profile: [
|
|
{
|
|
title: 'Unsaved Profile Changes',
|
|
message: 'Unsaved changes to the current profile will be lost.\n Continue?',
|
|
checkPrompt: (setting, newVal, oldVal) => {
|
|
return !!chatSettings.isDirty && newVal !== oldVal
|
|
},
|
|
passed: false
|
|
}
|
|
]
|
|
}
|
|
|
|
const resetSettingCheck = (key:keyof ChatSettings) => {
|
|
const checks = settingChecks[key]
|
|
checks && checks.forEach((c) => { c.passed = false })
|
|
}
|
|
|
|
const queueSettingValueChange = (event: Event, setting: ChatSetting) => {
|
|
if (event.target === null) return
|
|
const val = chatSettings[setting.key]
|
|
const el = (event.target as HTMLInputElement)
|
|
const doSet = () => {
|
|
try {
|
|
(typeof setting.beforeChange === 'function') && setting.beforeChange(chatId, setting, el.checked || el.value) &&
|
|
refreshSettings()
|
|
} catch (e) {
|
|
errorNotice('Unable to change:', e)
|
|
}
|
|
switch (setting.type) {
|
|
case 'boolean':
|
|
setChatSettingValue(chatId, setting, el.checked)
|
|
refreshSettings()
|
|
break
|
|
default:
|
|
setChatSettingValue(chatId, setting, el.value)
|
|
}
|
|
const newVal = cleanSettingValue(setting.type, el.checked || el.value)
|
|
if (val === newVal) return
|
|
try {
|
|
if ((typeof setting.afterChange === 'function') && setting.afterChange(chatId, setting, chatSettings[setting.key])) {
|
|
// console.log('Refreshed from setting', setting.key, chatSettings[setting.key], val)
|
|
refreshSettings()
|
|
}
|
|
} catch (e) {
|
|
setChatSettingValue(chatId, setting, val)
|
|
errorNotice('Unable to change:', e)
|
|
}
|
|
dispatch('change', setting)
|
|
}
|
|
const checks = settingChecks[setting.key] || []
|
|
const newVal = cleanSettingValue(setting.type, el.checked || el.value)
|
|
for (let i = 0, l = checks.length; i < l; i++) {
|
|
const c = checks[i]
|
|
if (c.passed) continue
|
|
if (c.checkPrompt(setting, newVal, val)) {
|
|
openModal(PromptConfirm, {
|
|
title: c.title,
|
|
message: c.message,
|
|
class: c.class || 'is-warning',
|
|
onConfirm: () => {
|
|
c.passed = true
|
|
if (c.onYes && c.onYes(setting, newVal, val)) {
|
|
resetSettingCheck(setting.key)
|
|
} else {
|
|
queueSettingValueChange(event, setting)
|
|
}
|
|
},
|
|
onCancel: () => {
|
|
// roll-back
|
|
if (!c.onNo || !c.onNo(setting, newVal, val)) {
|
|
resetSettingCheck(setting.key)
|
|
setChatSettingValue(chatId, setting, val)
|
|
// refresh setting modal, if open
|
|
c.onNo && c.onNo(setting, newVal, val)
|
|
refreshSettings()
|
|
} else {
|
|
queueSettingValueChange(event, setting)
|
|
}
|
|
}
|
|
})
|
|
} else {
|
|
c.passed = true
|
|
}
|
|
}
|
|
// passed all?
|
|
if (checks.find(c => !c.passed)) return
|
|
resetSettingCheck(setting.key)
|
|
doSet()
|
|
}
|
|
|
|
</script>
|
|
|
|
{#if show}
|
|
{#if header}
|
|
<p class="notification {headerClass}">
|
|
{@html header}
|
|
</p>
|
|
{/if}
|
|
<div class="field is-horizontal">
|
|
{#if setting.type === 'boolean'}
|
|
<div class="field is-normal">
|
|
<label class="label" for="settings-{setting.key}" title="{setting.title}">
|
|
<input
|
|
type="checkbox"
|
|
title="{setting.title}"
|
|
class="checkbox"
|
|
id="settings-{setting.key}"
|
|
checked={!!chatSettings[setting.key]}
|
|
on:click={e => queueSettingValueChange(e, setting)}
|
|
>
|
|
{setting.name}
|
|
</label>
|
|
</div>
|
|
{:else if setting.type === 'textarea'}
|
|
<div class="field is-normal" style="width:100%">
|
|
<label class="label" for="settings-{setting.key}" title="{setting.title}">{setting.name}</label>
|
|
<textarea
|
|
class="input is-info is-focused chat-input auto-size"
|
|
placeholder={placeholder || ''}
|
|
rows="1"
|
|
on:input={e => autoGrowInputOnEvent(e)}
|
|
on:change={e => { queueSettingValueChange(e, setting); autoGrowInputOnEvent(e) }}
|
|
>{chatSettings[setting.key]}</textarea>
|
|
</div>
|
|
{:else}
|
|
<div class="field-label is-normal">
|
|
<label class="label" for="settings-{setting.key}" title="{setting.title}">{setting.name}</label>
|
|
</div>
|
|
{/if}
|
|
<div class="field-body">
|
|
<div class="field" class:has-addons={fieldControls.length}>
|
|
{#if setting.type === 'number'}
|
|
<input
|
|
class="input"
|
|
inputmode="decimal"
|
|
type={setting.type}
|
|
title="{setting.title}"
|
|
id="settings-{setting.key}"
|
|
value={chatSettings[setting.key]}
|
|
min={setting.min}
|
|
max={setting.max}
|
|
step={setting.step}
|
|
placeholder={String(placeholder || chatDefaults[setting.key])}
|
|
on:change={e => queueSettingValueChange(e, setting)}
|
|
/>
|
|
{:else if setting.type === 'select' || setting.type === 'select-number'}
|
|
<!-- <div class="select"> -->
|
|
<div class="select" class:control={fieldControls.length}>
|
|
{#key rkey}
|
|
<select id="settings-{setting.key}" title="{setting.title}" on:change={e => queueSettingValueChange(e, setting) } >
|
|
{#each setting.options as option}
|
|
<option class:is-default={option.value === chatDefaults[setting.key]} value={option.value} selected={option.value === chatSettings[setting.key]} disabled={option.disabled}>{option.text}</option>
|
|
{/each}
|
|
</select>
|
|
{/key}
|
|
</div>
|
|
{#each fieldControls as cont}
|
|
<div class="control">
|
|
<button title={cont.text} on:click={() => { cont.action && cont.action(chatId, setting, chatSettings[setting.key]); refreshSettings() }} class="button {cont.class || ''}">
|
|
{#if cont.text}
|
|
<span class="text">
|
|
<Fa icon={cont.icon} />
|
|
</span>
|
|
{/if}
|
|
{#if cont.icon}
|
|
<span class="icon">
|
|
<Fa icon={cont.icon} />
|
|
</span>
|
|
{/if}
|
|
</button>
|
|
</div>
|
|
{/each}
|
|
|
|
{:else if setting.type === 'text'}
|
|
<div class="field">
|
|
<input
|
|
type="text"
|
|
title="{setting.title}"
|
|
class="input"
|
|
value={chatSettings[setting.key]}
|
|
placeholder={String(placeholder || chatDefaults[setting.key])}
|
|
on:change={e => { queueSettingValueChange(e, setting) }}
|
|
>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{/if} |