import type { ApplogValue, ArrayElementType, EntityID, Thread } from '@wovin/core' import type { Component } from 'solid-js' import type { BlockVM } from '../data/VMs/BlockVM' import type { IconName } from './mini-components' import { querySingleAndMap } from '@wovin/core' import { Logger } from 'besonders-logger' import get from 'lodash-es/get' import set from 'lodash-es/set' import { createMemo, createSignal, Show } from 'solid-js' import { hideBlockSettings } from '../App' import { useCurrentThread } from '../ui/reactive' import { useLogWriter } from '../ui/utils-ui' import { HistoryDialog } from './HistoryDialog' import { ButtonGroup, FlexBetween } from './mini-components' const { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO, { prefix: '[BlockSettingsDialog]' }) export const BLOCK_SETTING_NAMES = [ 'smart_list/exclusive_groups', 'smart_list/hide_parent_tags', 'block/type', ] as const const buttonData = (icon = 'info-bold' as IconName, click = e => LOG('?', e), restProps = {}) => ({ icon, click, restProps }) export const BlockSettingsRow: Component<{ blockID: EntityID | null thread: Thread blockOrFake: Partial }> = (props) => { // const blkVM = useBlk(props.blockID) // removed to accomodate potential fakeness const [isTabs, setTabs] = createSignal(props.blockOrFake.type === 'tabs') // TODO make this signal obselete and use the block prop const [isSurvey, setSurvey] = createSignal(props.blockOrFake.type === 'survey') // TODO make this signal obselete and use the block prop const [isHistoryOpen, setHistory] = createSignal(false) // TODO make this signal obselete and use the block prop const removeSmartList = e => props.blockOrFake.toggleSmartlist?.() ?? WARN('missing toggleSmartlist, removeSmartList on fake block?', props.blockOrFake) const restPropsForSmart = createMemo(() => props.blockOrFake.type === 'smartlist' ? { variant: 'primary' } : {}) const restPropsForTabs = createMemo(() => isTabs() ? { variant: 'primary' } : {}) const restPropsForSurvey = createMemo(() => isSurvey() ? { variant: 'primary' } : {}) const toggleTabsType = (e) => { DEBUG('toggle tabs', e, restPropsForTabs(), isTabs(), props.blockOrFake) const newValue = !isTabs() setTabs(newValue) if (props.blockOrFake.type === 'tabs') { props.blockOrFake.type = 'text' } else { props.blockOrFake.type = 'tabs' } } const toggleSurveyType = (e) => { DEBUG('toggle survey', e, restPropsForTabs(), isSurvey(), props.blockOrFake) const newValue = !isSurvey() setSurvey(newValue) if (props.blockOrFake.type === 'survey') { props.blockOrFake.type = 'text' } else { props.blockOrFake.type = 'survey' } } const historyDialogRefHolder = { ref: null } const openHistoryDialog = (e: PointerEvent) => { DEBUG('hist', { e, historyDialogRef: historyDialogRefHolder }) setHistory(true) historyDialogRefHolder.ref?.show() } const leftGroup = [ // eslint-disable-next-line solid/reactivity makeSettingButton('block/type', 'text', (val) => { DEBUG('smartlist button', val) return { icon: 'list-magnifying-glass', restProps: { title: `toggle smartlist`, variant: val === 'smartlist' ? 'primary' : 'default', }, } }, (setTing) => { return currentVal => currentVal === 'smartlist' ? setTing('text') : setTing('smartlist') }), // buttonData('list-magnifying-glass', removeSmartList, restPropsForSmart()), buttonData('tabs', toggleTabsType, restPropsForTabs()), buttonData('list-checks', toggleSurveyType, restPropsForSurvey()), buttonData('gear-bold'), buttonData('clock-counter-clockwise', openHistoryDialog), buttonData(), ] const rightGroup = createMemo(() => { return [ makeSettingButton('smart_list/hide_parent_tags', true, val => ({ icon: val ? 'eye-slash' : 'eye', restProps: { title: `tags of smartlist query in kids are ${val ? '' : 'not '}hidden` }, })), makeSettingButton('smart_list/exclusive_groups', false, val => ({ icon: 'exclude-duotone', restProps: { title: `smartlist groups are ${val ? '' : 'not '}exclusive` }, })), ] }) return ( ) function makeSettingButton( setting: ArrayElementType, defaultValue, propsBuilder: (val: boolean | string) => Record, setterBuilder?: (setTing) => (val: boolean | string) => typeof val, ) { let [getTing, setTing]: ReturnType> = [null, null] if (props.blockID) { ;[getTing, setTing] = useBlockSetting(props.blockID, setting, defaultValue) } else { ;[getTing, setTing] = [ () => get(props.blockOrFake, `setting.${setting}`), newVal => set(props.blockOrFake, `setting.${setting}`, newVal), ] } VERBOSE(`[makeSettingButton#${props.blockID}]`, { getTing, setTing }) const setter = setterBuilder ? setterBuilder(setTing) : currentVal => setTing(!currentVal) return { restProps: { variant: getTing() ? 'primary' : 'default' }, click: (e: MouseEvent) => { if (e.button === 0) { setter(getTing()) e.preventDefault() return true } }, ...propsBuilder?.(getTing()), } } } export const BlockSettingsDialog: Component<{ blockID: EntityID | '' }> = (props) => { // DEBUG('render', props.blockID, blockSettingsID()) // if (!props.blockID) return null return ( hideBlockSettings()} >

BlockSettings for {`${props.blockID}`}

BlockSettings
) } export function useBlockSetting(blockID: EntityID, setting: ArrayElementType, defaultValue: T) { if (!blockID) throw ERROR(`[useBlockSetting] Invalid blockID:`, blockID) const BASE = { en: blockID, at: `${setting}` } const val = querySingleAndMap(useCurrentThread(), BASE, 'vl') const addLogs = useLogWriter() return [ () => (val.get() === undefined) ? defaultValue : val.get() as T, // (i) we're checking our code, not if the data is valid (newVal: T) => addLogs({ ...BASE, vl: newVal }), ] as const }