You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
112 lines
4.6 KiB
112 lines
4.6 KiB
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<PropertyT extends string & keyof EntryMainMenuOptions> {
|
|
readonly type: "mainMenu"
|
|
readonly property: PropertyT
|
|
choice(input: EntryMainMenuOptions[PropertyT]): ExpandChoiceOptions & { value: PropertyT }
|
|
onSelected(input: EntryMainMenuOptions[PropertyT]): Promise<EntryMainMenuOptions[PropertyT]>
|
|
}
|
|
function getChoice<PropertyT extends string & keyof EntryMainMenuOptions>(choice: EntryMainMenuChoice<PropertyT>, entry: EntryMainMenuOptions): ExpandChoiceOptions & {value: PropertyT} {
|
|
return choice.choice(entry[choice.property])
|
|
}
|
|
async function onSelected<PropertyT extends string & keyof EntryMainMenuOptions>(choice: EntryMainMenuChoice<PropertyT>, entry: EntryMainMenuOptions): Promise<Partial<EntryMainMenuOptions>> {
|
|
return {
|
|
[choice.property]: await choice.onSelected(entry[choice.property])
|
|
}
|
|
}
|
|
|
|
export function makeEntryMainMenuChoice<PropertyT extends string & keyof EntryMainMenuOptions, OptionsT, OutputT>({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<OutputT>,
|
|
toOptions: (input: EntryMainMenuOptions[PropertyT]) => OptionsT,
|
|
toProperty: (output: OutputT) => EntryMainMenuOptions[PropertyT]
|
|
}): {
|
|
(options: OptionsT): Promise<OutputT>,
|
|
mainMenu: EntryMainMenuChoice<PropertyT>
|
|
} {
|
|
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<EntryMainMenuOptions[PropertyT]> {
|
|
const output = await injected(toOptions(input))
|
|
return toProperty(output)
|
|
},
|
|
}
|
|
})
|
|
}
|
|
|
|
export interface EntryMainMenuOptions extends Omit<Entry, "finishedAt"> {}
|
|
|
|
export interface EntryMainMenuDependencies {
|
|
readonly inquire: InquireFunction
|
|
readonly choices: readonly (EntryMainMenuChoice<any>|SeparatorOptions)[]
|
|
readonly showError: ShowFunction
|
|
}
|
|
|
|
export function entryMainMenuPrompt(deps: EntryMainMenuDependencies): (options: EntryMainMenuOptions) => Promise<Entry> {
|
|
return (options) => promptForEntryMainMenu(options, deps)
|
|
}
|
|
|
|
export async function promptForEntryMainMenu(entry: EntryMainMenuOptions, deps: EntryMainMenuDependencies): Promise<Entry> {
|
|
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<EntryMainMenuOptions>): Promise<Entry> {
|
|
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<any>
|
|
return continueWith(await onSelected(selectedChoice, entry))
|
|
}
|
|
} |