import {InquireFunction, ShowFunction} from "../Inquire"; import {ExpandChoiceOptions, Separator, SeparatorOptions} from "inquirer"; import {merge} from "../../utils/Merge"; import {Entry, EntryJTD} from "../../datatypes/Entry"; import {editYaml} from "../../schemata/YAMLPrompt"; const VIEW = ".View" const DONE = ".Done" export interface EntryMainMenuChoice { readonly type: "mainMenu" readonly property: PropertyT choice(input: EntryMainMenuOptions[PropertyT]): ExpandChoiceOptions & { value: PropertyT } onSelected(input: EntryMainMenuOptions[PropertyT]): Promise } function getChoice(choice: EntryMainMenuChoice, entry: EntryMainMenuOptions): ExpandChoiceOptions & {value: PropertyT} { return choice.choice(entry[choice.property]) } async function onSelected(choice: EntryMainMenuChoice, entry: EntryMainMenuOptions): Promise> { return { [choice.property]: await choice.onSelected(entry[choice.property]) } } export function makeEntryMainMenuChoice({property, key, name, short, injected, toOptions, toProperty}: { property: PropertyT, key: string, name: string|((value: EntryMainMenuOptions[PropertyT]) => string), short?: string|((value: EntryMainMenuOptions[PropertyT]) => string), injected: (options: OptionsT) => Promise, toOptions: (input: EntryMainMenuOptions[PropertyT]) => OptionsT, toProperty: (output: OutputT) => EntryMainMenuOptions[PropertyT] }): { (options: OptionsT): Promise, mainMenu: EntryMainMenuChoice } { return Object.assign(injected, { "mainMenu": { type: "mainMenu" as const, property, choice(input: EntryMainMenuOptions[PropertyT]) { return { name: typeof name === "string" ? name : name(input), short: typeof short === "string" ? short : typeof short === "function" ? short(input) : typeof name === "string" ? name : name(input), value: property as PropertyT, key, } }, async onSelected(input: EntryMainMenuOptions[PropertyT]): Promise { const output = await injected(toOptions(input)) return toProperty(output) }, } }) } export interface EntryMainMenuOptions extends Omit {} export interface EntryMainMenuDependencies { readonly inquire: InquireFunction readonly choices: readonly (EntryMainMenuChoice|SeparatorOptions)[] readonly showError: ShowFunction } export function entryMainMenuPrompt(deps: EntryMainMenuDependencies): (options: EntryMainMenuOptions) => Promise { return (options) => promptForEntryMainMenu(options, deps) } export async function promptForEntryMainMenu(entry: EntryMainMenuOptions, deps: EntryMainMenuDependencies): Promise { const { inquire, choices, showError, } = deps const result = await inquire({ type: "expand", message: "Anything else you want to add?", pageSize: 999, choices: [ ...choices.map((choice) => choice.type === "separator" ? choice : getChoice(choice, entry)), new Separator(), {name: "View and edit this entry as yaml", value: VIEW, key: "v"}, {name: "Save and upload this entry", value: DONE, key: "q"}, new Separator(), ] }) function continueWith(update: Partial): Promise { const data = merge(update, entry) return promptForEntryMainMenu(data, deps) } if (result === DONE) { return { ...entry, finishedAt: new Date(), } } else if (result === VIEW) { const updated = await editYaml({ schema: EntryJTD, currentValue: {...entry, finishedAt: new Date()}, name: "the current entry", inquire, showError, }) const withoutFinishedAt = {...updated, finishedAt: undefined} return continueWith(withoutFinishedAt) } else { const selectedChoice = choices.find((choice) => choice.type === "mainMenu" && choice.property === result) as EntryMainMenuChoice return continueWith(await onSelected(selectedChoice, entry)) } }