@azurioh/discord-kernel 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +77 -0
- package/dist/authz/authorizer.d.ts +45 -0
- package/dist/authz/authorizer.d.ts.map +1 -0
- package/dist/authz/authorizer.js +31 -0
- package/dist/authz/authorizer.js.map +1 -0
- package/dist/authz/require-level-guard.d.ts +23 -0
- package/dist/authz/require-level-guard.d.ts.map +1 -0
- package/dist/authz/require-level-guard.js +37 -0
- package/dist/authz/require-level-guard.js.map +1 -0
- package/dist/clock.d.ts +13 -0
- package/dist/clock.d.ts.map +1 -0
- package/dist/clock.js +13 -0
- package/dist/clock.js.map +1 -0
- package/dist/concurrency/keyed-queue.d.ts +18 -0
- package/dist/concurrency/keyed-queue.d.ts.map +1 -0
- package/dist/concurrency/keyed-queue.js +37 -0
- package/dist/concurrency/keyed-queue.js.map +1 -0
- package/dist/concurrency/run-with-concurrency.d.ts +15 -0
- package/dist/concurrency/run-with-concurrency.d.ts.map +1 -0
- package/dist/concurrency/run-with-concurrency.js +40 -0
- package/dist/concurrency/run-with-concurrency.js.map +1 -0
- package/dist/config/env.d.ts +17 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/env.js +49 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/errors.d.ts +14 -0
- package/dist/config/errors.d.ts.map +1 -0
- package/dist/config/errors.js +25 -0
- package/dist/config/errors.js.map +1 -0
- package/dist/datetime.d.ts +34 -0
- package/dist/datetime.d.ts.map +1 -0
- package/dist/datetime.js +188 -0
- package/dist/datetime.js.map +1 -0
- package/dist/discord/api-errors.d.ts +20 -0
- package/dist/discord/api-errors.d.ts.map +1 -0
- package/dist/discord/api-errors.js +90 -0
- package/dist/discord/api-errors.js.map +1 -0
- package/dist/discord/channel-exporter.d.ts +42 -0
- package/dist/discord/channel-exporter.d.ts.map +1 -0
- package/dist/discord/channel-exporter.js +3 -0
- package/dist/discord/channel-exporter.js.map +1 -0
- package/dist/discord/command/autocomplete-dispatcher.d.ts +26 -0
- package/dist/discord/command/autocomplete-dispatcher.d.ts.map +1 -0
- package/dist/discord/command/autocomplete-dispatcher.js +41 -0
- package/dist/discord/command/autocomplete-dispatcher.js.map +1 -0
- package/dist/discord/command/autocomplete.d.ts +18 -0
- package/dist/discord/command/autocomplete.d.ts.map +1 -0
- package/dist/discord/command/autocomplete.js +65 -0
- package/dist/discord/command/autocomplete.js.map +1 -0
- package/dist/discord/command/context.d.ts +33 -0
- package/dist/discord/command/context.d.ts.map +1 -0
- package/dist/discord/command/context.js +48 -0
- package/dist/discord/command/context.js.map +1 -0
- package/dist/discord/command/cooldown-guard.d.ts +25 -0
- package/dist/discord/command/cooldown-guard.d.ts.map +1 -0
- package/dist/discord/command/cooldown-guard.js +60 -0
- package/dist/discord/command/cooldown-guard.js.map +1 -0
- package/dist/discord/command/create-command.d.ts +100 -0
- package/dist/discord/command/create-command.d.ts.map +1 -0
- package/dist/discord/command/create-command.js +245 -0
- package/dist/discord/command/create-command.js.map +1 -0
- package/dist/discord/command/create-context-menu-command.d.ts +51 -0
- package/dist/discord/command/create-context-menu-command.d.ts.map +1 -0
- package/dist/discord/command/create-context-menu-command.js +128 -0
- package/dist/discord/command/create-context-menu-command.js.map +1 -0
- package/dist/discord/command/errors.d.ts +19 -0
- package/dist/discord/command/errors.d.ts.map +1 -0
- package/dist/discord/command/errors.js +27 -0
- package/dist/discord/command/errors.js.map +1 -0
- package/dist/discord/command/guard.d.ts +30 -0
- package/dist/discord/command/guard.d.ts.map +1 -0
- package/dist/discord/command/guard.js +34 -0
- package/dist/discord/command/guard.js.map +1 -0
- package/dist/discord/command/localization.d.ts +9 -0
- package/dist/discord/command/localization.d.ts.map +1 -0
- package/dist/discord/command/localization.js +13 -0
- package/dist/discord/command/localization.js.map +1 -0
- package/dist/discord/command/options.d.ts +113 -0
- package/dist/discord/command/options.d.ts.map +1 -0
- package/dist/discord/command/options.js +509 -0
- package/dist/discord/command/options.js.map +1 -0
- package/dist/discord/command/permission-guard.d.ts +21 -0
- package/dist/discord/command/permission-guard.d.ts.map +1 -0
- package/dist/discord/command/permission-guard.js +69 -0
- package/dist/discord/command/permission-guard.js.map +1 -0
- package/dist/discord/command/router.d.ts +48 -0
- package/dist/discord/command/router.d.ts.map +1 -0
- package/dist/discord/command/router.js +118 -0
- package/dist/discord/command/router.js.map +1 -0
- package/dist/discord/command/types.d.ts +51 -0
- package/dist/discord/command/types.d.ts.map +1 -0
- package/dist/discord/command/types.js +3 -0
- package/dist/discord/command/types.js.map +1 -0
- package/dist/discord/components/component-router.d.ts +116 -0
- package/dist/discord/components/component-router.d.ts.map +1 -0
- package/dist/discord/components/component-router.js +123 -0
- package/dist/discord/components/component-router.js.map +1 -0
- package/dist/discord/components/interactive-message/index.d.ts +3 -0
- package/dist/discord/components/interactive-message/index.d.ts.map +1 -0
- package/dist/discord/components/interactive-message/index.js +11 -0
- package/dist/discord/components/interactive-message/index.js.map +1 -0
- package/dist/discord/components/interactive-message/interactive-message-collector.d.ts +195 -0
- package/dist/discord/components/interactive-message/interactive-message-collector.d.ts.map +1 -0
- package/dist/discord/components/interactive-message/interactive-message-collector.js +151 -0
- package/dist/discord/components/interactive-message/interactive-message-collector.js.map +1 -0
- package/dist/discord/components/interactive-message/mount-interactive-message.d.ts +30 -0
- package/dist/discord/components/interactive-message/mount-interactive-message.d.ts.map +1 -0
- package/dist/discord/components/interactive-message/mount-interactive-message.js +44 -0
- package/dist/discord/components/interactive-message/mount-interactive-message.js.map +1 -0
- package/dist/discord/components/paginator/index.d.ts +5 -0
- package/dist/discord/components/paginator/index.d.ts.map +1 -0
- package/dist/discord/components/paginator/index.js +15 -0
- package/dist/discord/components/paginator/index.js.map +1 -0
- package/dist/discord/components/paginator/mount-paginator.d.ts +25 -0
- package/dist/discord/components/paginator/mount-paginator.d.ts.map +1 -0
- package/dist/discord/components/paginator/mount-paginator.js +41 -0
- package/dist/discord/components/paginator/mount-paginator.js.map +1 -0
- package/dist/discord/components/paginator/page-state.d.ts +21 -0
- package/dist/discord/components/paginator/page-state.d.ts.map +1 -0
- package/dist/discord/components/paginator/page-state.js +44 -0
- package/dist/discord/components/paginator/page-state.js.map +1 -0
- package/dist/discord/components/paginator/paginator-buttons.d.ts +17 -0
- package/dist/discord/components/paginator/paginator-buttons.d.ts.map +1 -0
- package/dist/discord/components/paginator/paginator-buttons.js +45 -0
- package/dist/discord/components/paginator/paginator-buttons.js.map +1 -0
- package/dist/discord/components/paginator/paginator-view.d.ts +26 -0
- package/dist/discord/components/paginator/paginator-view.d.ts.map +1 -0
- package/dist/discord/components/paginator/paginator-view.js +28 -0
- package/dist/discord/components/paginator/paginator-view.js.map +1 -0
- package/dist/discord/components/persistent-paginator.d.ts +47 -0
- package/dist/discord/components/persistent-paginator.d.ts.map +1 -0
- package/dist/discord/components/persistent-paginator.js +137 -0
- package/dist/discord/components/persistent-paginator.js.map +1 -0
- package/dist/discord/components/settings-editor/index.d.ts +7 -0
- package/dist/discord/components/settings-editor/index.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/index.js +22 -0
- package/dist/discord/components/settings-editor/index.js.map +1 -0
- package/dist/discord/components/settings-editor/mount-settings-editor.d.ts +140 -0
- package/dist/discord/components/settings-editor/mount-settings-editor.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/mount-settings-editor.js +344 -0
- package/dist/discord/components/settings-editor/mount-settings-editor.js.map +1 -0
- package/dist/discord/components/settings-editor/navigation.d.ts +49 -0
- package/dist/discord/components/settings-editor/navigation.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/navigation.js +39 -0
- package/dist/discord/components/settings-editor/navigation.js.map +1 -0
- package/dist/discord/components/settings-editor/settings-editor-card.view.d.ts +56 -0
- package/dist/discord/components/settings-editor/settings-editor-card.view.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/settings-editor-card.view.js +284 -0
- package/dist/discord/components/settings-editor/settings-editor-card.view.js.map +1 -0
- package/dist/discord/components/settings-editor/settings-editor-field-modal.d.ts +69 -0
- package/dist/discord/components/settings-editor/settings-editor-field-modal.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/settings-editor-field-modal.js +353 -0
- package/dist/discord/components/settings-editor/settings-editor-field-modal.js.map +1 -0
- package/dist/discord/components/settings-editor/settings-editor-fields.d.ts +216 -0
- package/dist/discord/components/settings-editor/settings-editor-fields.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/settings-editor-fields.js +66 -0
- package/dist/discord/components/settings-editor/settings-editor-fields.js.map +1 -0
- package/dist/discord/components/settings-editor/settings-editor.view.d.ts +303 -0
- package/dist/discord/components/settings-editor/settings-editor.view.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/settings-editor.view.js +316 -0
- package/dist/discord/components/settings-editor/settings-editor.view.js.map +1 -0
- package/dist/discord/events/create-event.d.ts +5 -0
- package/dist/discord/events/create-event.d.ts.map +1 -0
- package/dist/discord/events/create-event.js +8 -0
- package/dist/discord/events/create-event.js.map +1 -0
- package/dist/discord/events/event-router.d.ts +16 -0
- package/dist/discord/events/event-router.d.ts.map +1 -0
- package/dist/discord/events/event-router.js +51 -0
- package/dist/discord/events/event-router.js.map +1 -0
- package/dist/discord/events/types.d.ts +16 -0
- package/dist/discord/events/types.d.ts.map +1 -0
- package/dist/discord/events/types.js +3 -0
- package/dist/discord/events/types.js.map +1 -0
- package/dist/discord/i18n.d.ts +58 -0
- package/dist/discord/i18n.d.ts.map +1 -0
- package/dist/discord/i18n.js +173 -0
- package/dist/discord/i18n.js.map +1 -0
- package/dist/discord/interaction/button.d.ts +39 -0
- package/dist/discord/interaction/button.d.ts.map +1 -0
- package/dist/discord/interaction/button.js +36 -0
- package/dist/discord/interaction/button.js.map +1 -0
- package/dist/discord/interaction/interaction-router.d.ts +21 -0
- package/dist/discord/interaction/interaction-router.d.ts.map +1 -0
- package/dist/discord/interaction/interaction-router.js +24 -0
- package/dist/discord/interaction/interaction-router.js.map +1 -0
- package/dist/discord/interaction/modal.d.ts +171 -0
- package/dist/discord/interaction/modal.d.ts.map +1 -0
- package/dist/discord/interaction/modal.js +264 -0
- package/dist/discord/interaction/modal.js.map +1 -0
- package/dist/discord/interaction/select-menu.d.ts +104 -0
- package/dist/discord/interaction/select-menu.d.ts.map +1 -0
- package/dist/discord/interaction/select-menu.js +126 -0
- package/dist/discord/interaction/select-menu.js.map +1 -0
- package/dist/discord/permissions.d.ts +32 -0
- package/dist/discord/permissions.d.ts.map +1 -0
- package/dist/discord/permissions.js +40 -0
- package/dist/discord/permissions.js.map +1 -0
- package/dist/discord/placeholder-registry-errors.d.ts +10 -0
- package/dist/discord/placeholder-registry-errors.d.ts.map +1 -0
- package/dist/discord/placeholder-registry-errors.js +18 -0
- package/dist/discord/placeholder-registry-errors.js.map +1 -0
- package/dist/discord/placeholder-registry.d.ts +53 -0
- package/dist/discord/placeholder-registry.d.ts.map +1 -0
- package/dist/discord/placeholder-registry.js +52 -0
- package/dist/discord/placeholder-registry.js.map +1 -0
- package/dist/discord/presenter.d.ts +26 -0
- package/dist/discord/presenter.d.ts.map +1 -0
- package/dist/discord/presenter.js +3 -0
- package/dist/discord/presenter.js.map +1 -0
- package/dist/discord/ui/card.d.ts +114 -0
- package/dist/discord/ui/card.d.ts.map +1 -0
- package/dist/discord/ui/card.js +162 -0
- package/dist/discord/ui/card.js.map +1 -0
- package/dist/discord/ui/color-input-errors.d.ts +12 -0
- package/dist/discord/ui/color-input-errors.d.ts.map +1 -0
- package/dist/discord/ui/color-input-errors.js +21 -0
- package/dist/discord/ui/color-input-errors.js.map +1 -0
- package/dist/discord/ui/color-input.d.ts +37 -0
- package/dist/discord/ui/color-input.d.ts.map +1 -0
- package/dist/discord/ui/color-input.js +166 -0
- package/dist/discord/ui/color-input.js.map +1 -0
- package/dist/discord/ui/colors.d.ts +44 -0
- package/dist/discord/ui/colors.d.ts.map +1 -0
- package/dist/discord/ui/colors.js +45 -0
- package/dist/discord/ui/colors.js.map +1 -0
- package/dist/discord/ui/embed.d.ts +16 -0
- package/dist/discord/ui/embed.d.ts.map +1 -0
- package/dist/discord/ui/embed.js +53 -0
- package/dist/discord/ui/embed.js.map +1 -0
- package/dist/discord/ui/message-attachments.d.ts +40 -0
- package/dist/discord/ui/message-attachments.d.ts.map +1 -0
- package/dist/discord/ui/message-attachments.js +27 -0
- package/dist/discord/ui/message-attachments.js.map +1 -0
- package/dist/errors/business-error.d.ts +41 -0
- package/dist/errors/business-error.d.ts.map +1 -0
- package/dist/errors/business-error.js +39 -0
- package/dist/errors/business-error.js.map +1 -0
- package/dist/errors/describe-error.d.ts +29 -0
- package/dist/errors/describe-error.d.ts.map +1 -0
- package/dist/errors/describe-error.js +49 -0
- package/dist/errors/describe-error.js.map +1 -0
- package/dist/errors/incident-ref.d.ts +7 -0
- package/dist/errors/incident-ref.d.ts.map +1 -0
- package/dist/errors/incident-ref.js +13 -0
- package/dist/errors/incident-ref.js.map +1 -0
- package/dist/errors/index.d.ts +4 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +15 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/i18n/business-message.d.ts +14 -0
- package/dist/i18n/business-message.d.ts.map +1 -0
- package/dist/i18n/business-message.js +19 -0
- package/dist/i18n/business-message.js.map +1 -0
- package/dist/i18n/catalog.d.ts +27 -0
- package/dist/i18n/catalog.d.ts.map +1 -0
- package/dist/i18n/catalog.js +25 -0
- package/dist/i18n/catalog.js.map +1 -0
- package/dist/i18n/errors.d.ts +10 -0
- package/dist/i18n/errors.d.ts.map +1 -0
- package/dist/i18n/errors.js +18 -0
- package/dist/i18n/errors.js.map +1 -0
- package/dist/i18n/index.d.ts +6 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +16 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/locale.d.ts +22 -0
- package/dist/i18n/locale.d.ts.map +1 -0
- package/dist/i18n/locale.js +40 -0
- package/dist/i18n/locale.js.map +1 -0
- package/dist/i18n/translator.d.ts +35 -0
- package/dist/i18n/translator.d.ts.map +1 -0
- package/dist/i18n/translator.js +40 -0
- package/dist/i18n/translator.js.map +1 -0
- package/dist/logger.d.ts +28 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +3 -0
- package/dist/logger.js.map +1 -0
- package/dist/module/module.d.ts +42 -0
- package/dist/module/module.d.ts.map +1 -0
- package/dist/module/module.js +3 -0
- package/dist/module/module.js.map +1 -0
- package/dist/persistence/database.d.ts +24 -0
- package/dist/persistence/database.d.ts.map +1 -0
- package/dist/persistence/database.js +3 -0
- package/dist/persistence/database.js.map +1 -0
- package/dist/resilience/retry.d.ts +36 -0
- package/dist/resilience/retry.d.ts.map +1 -0
- package/dist/resilience/retry.js +78 -0
- package/dist/resilience/retry.js.map +1 -0
- package/dist/scheduler/errors.d.ts +25 -0
- package/dist/scheduler/errors.d.ts.map +1 -0
- package/dist/scheduler/errors.js +45 -0
- package/dist/scheduler/errors.js.map +1 -0
- package/dist/scheduler/scheduler.d.ts +68 -0
- package/dist/scheduler/scheduler.d.ts.map +1 -0
- package/dist/scheduler/scheduler.js +162 -0
- package/dist/scheduler/scheduler.js.map +1 -0
- package/package.json +89 -0
- package/src/authz/authorizer.ts +76 -0
- package/src/authz/require-level-guard.ts +42 -0
- package/src/clock.ts +18 -0
- package/src/concurrency/keyed-queue.ts +50 -0
- package/src/concurrency/run-with-concurrency.ts +44 -0
- package/src/config/env.ts +58 -0
- package/src/config/errors.ts +21 -0
- package/src/datetime.ts +221 -0
- package/src/discord/api-errors.ts +91 -0
- package/src/discord/channel-exporter.ts +48 -0
- package/src/discord/command/autocomplete-dispatcher.ts +54 -0
- package/src/discord/command/autocomplete.ts +76 -0
- package/src/discord/command/context.ts +85 -0
- package/src/discord/command/cooldown-guard.ts +81 -0
- package/src/discord/command/create-command.ts +431 -0
- package/src/discord/command/create-context-menu-command.ts +227 -0
- package/src/discord/command/errors.ts +22 -0
- package/src/discord/command/guard.ts +54 -0
- package/src/discord/command/localization.ts +11 -0
- package/src/discord/command/options.ts +847 -0
- package/src/discord/command/permission-guard.ts +74 -0
- package/src/discord/command/router.ts +142 -0
- package/src/discord/command/types.ts +52 -0
- package/src/discord/components/component-router.ts +213 -0
- package/src/discord/components/interactive-message/index.ts +18 -0
- package/src/discord/components/interactive-message/interactive-message-collector.ts +374 -0
- package/src/discord/components/interactive-message/mount-interactive-message.ts +71 -0
- package/src/discord/components/paginator/index.ts +17 -0
- package/src/discord/components/paginator/mount-paginator.ts +69 -0
- package/src/discord/components/paginator/page-state.ts +45 -0
- package/src/discord/components/paginator/paginator-buttons.ts +60 -0
- package/src/discord/components/paginator/paginator-view.ts +61 -0
- package/src/discord/components/persistent-paginator.ts +197 -0
- package/src/discord/components/settings-editor/index.ts +41 -0
- package/src/discord/components/settings-editor/mount-settings-editor.ts +646 -0
- package/src/discord/components/settings-editor/navigation.ts +67 -0
- package/src/discord/components/settings-editor/settings-editor-card.view.ts +344 -0
- package/src/discord/components/settings-editor/settings-editor-field-modal.ts +538 -0
- package/src/discord/components/settings-editor/settings-editor-fields.ts +290 -0
- package/src/discord/components/settings-editor/settings-editor.view.ts +654 -0
- package/src/discord/events/create-event.ts +7 -0
- package/src/discord/events/event-router.ts +53 -0
- package/src/discord/events/types.ts +17 -0
- package/src/discord/i18n.ts +173 -0
- package/src/discord/interaction/button.ts +75 -0
- package/src/discord/interaction/interaction-router.ts +28 -0
- package/src/discord/interaction/modal.ts +505 -0
- package/src/discord/interaction/select-menu.ts +246 -0
- package/src/discord/permissions.ts +57 -0
- package/src/discord/placeholder-registry-errors.ts +11 -0
- package/src/discord/placeholder-registry.ts +77 -0
- package/src/discord/presenter.ts +26 -0
- package/src/discord/ui/card.ts +303 -0
- package/src/discord/ui/color-input-errors.ts +17 -0
- package/src/discord/ui/color-input.ts +179 -0
- package/src/discord/ui/colors.ts +58 -0
- package/src/discord/ui/embed.ts +64 -0
- package/src/discord/ui/message-attachments.ts +57 -0
- package/src/errors/business-error.ts +49 -0
- package/src/errors/describe-error.ts +73 -0
- package/src/errors/incident-ref.ts +10 -0
- package/src/errors/index.ts +12 -0
- package/src/i18n/business-message.ts +23 -0
- package/src/i18n/catalog.ts +41 -0
- package/src/i18n/errors.ts +11 -0
- package/src/i18n/index.ts +11 -0
- package/src/i18n/locale.ts +41 -0
- package/src/i18n/translator.ts +78 -0
- package/src/logger.ts +28 -0
- package/src/module/module.ts +42 -0
- package/src/persistence/database.ts +23 -0
- package/src/resilience/retry.ts +102 -0
- package/src/scheduler/errors.ts +40 -0
- package/src/scheduler/scheduler.ts +248 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.promptEditorFieldValue = promptEditorFieldValue;
|
|
4
|
+
exports.promptEditorGroupValue = promptEditorGroupValue;
|
|
5
|
+
const discord_js_1 = require("discord.js");
|
|
6
|
+
const settings_editor_fields_1 = require("../../../discord/components/settings-editor/settings-editor-fields");
|
|
7
|
+
const i18n_1 = require("../../../discord/i18n");
|
|
8
|
+
const modal_1 = require("../../../discord/interaction/modal");
|
|
9
|
+
const EDITOR_FIELD_MODAL_ID = "settings-editor:field-value";
|
|
10
|
+
const EDITOR_GROUP_MODAL_ID = "settings-editor:group-value";
|
|
11
|
+
/** Generous enough to write a whole embed description without racing a timeout. */
|
|
12
|
+
const MODAL_TIMEOUT_MS = 5 * 60 * 1000;
|
|
13
|
+
/**
|
|
14
|
+
* `createModal(...).build()` with no state produces the same constant
|
|
15
|
+
* customId for every field modal (`EDITOR_FIELD_MODAL_ID`) and every group
|
|
16
|
+
* modal (`EDITOR_GROUP_MODAL_ID`) — one identifier shared by the whole
|
|
17
|
+
* editor. Combined with a `filter` that only checked the user, a stale
|
|
18
|
+
* `awaitModalSubmit` left behind by a modal the administrator opened and then
|
|
19
|
+
* dismissed (it keeps listening for the rest of {@link MODAL_TIMEOUT_MS})
|
|
20
|
+
* would resolve on the *next* modal that same user submitted, whatever field
|
|
21
|
+
* it belonged to — reported as opening "Add a game", abandoning it, then
|
|
22
|
+
* declaring a request field named "Date" ending up creating a game named
|
|
23
|
+
* "Date".
|
|
24
|
+
*
|
|
25
|
+
* The fix needs an identifier that is unique per *opening*, not just per
|
|
26
|
+
* field: the field/group key alone is not enough, because the same field can
|
|
27
|
+
* be reopened while an earlier, dismissed opening's wait is still alive. A
|
|
28
|
+
* process-local monotonic counter appended to the key is enough for that —
|
|
29
|
+
* it only has to distinguish opens within this process's own lifetime, never
|
|
30
|
+
* survive a restart, and carries no notion of time, so a `Clock` would be the
|
|
31
|
+
* wrong tool (and untestable without faking time for no reason).
|
|
32
|
+
*
|
|
33
|
+
* Making the customId dynamic like this is safe *here*: unlike a persistent
|
|
34
|
+
* component, this modal is never registered on the `ComponentRouter` — both
|
|
35
|
+
* `promptEditorFieldValue` and `promptEditorGroupValue` collect their
|
|
36
|
+
* submission directly with `awaitModalSubmit`, a one-shot collector that
|
|
37
|
+
* matches by whatever customId it is told to expect, not by the router's
|
|
38
|
+
* `customId`/`customId:*` rule. A dynamic id would break the router (it needs
|
|
39
|
+
* a stable prefix to keep matching after a restart); it does not break a
|
|
40
|
+
* collector.
|
|
41
|
+
*/
|
|
42
|
+
let modalOpenSequence = 0;
|
|
43
|
+
/**
|
|
44
|
+
* A customId suffix unique to one modal opening: `key` for readability when
|
|
45
|
+
* debugging, plus a counter so reopening the same field still yields a
|
|
46
|
+
* different id from the opening before it.
|
|
47
|
+
*/
|
|
48
|
+
function nextModalOpenState(key) {
|
|
49
|
+
modalOpenSequence += 1;
|
|
50
|
+
return `${key}-${modalOpenSequence}`;
|
|
51
|
+
}
|
|
52
|
+
/** The customId a built modal actually carries, read back rather than reassembled. */
|
|
53
|
+
function customIdOf(modal) {
|
|
54
|
+
return modal.toJSON().custom_id;
|
|
55
|
+
}
|
|
56
|
+
/** One image per override — the embed has exactly one slot for each. */
|
|
57
|
+
const MAX_UPLOADS = 1;
|
|
58
|
+
/**
|
|
59
|
+
* Ask for one field's new value, prefilled with the current one, and wait for
|
|
60
|
+
* the answer. Returns `null` on timeout or on any failure to show/collect it
|
|
61
|
+
* (the administrator dismissed the modal, say) — the caller treats that as
|
|
62
|
+
* "nothing happened", never as an error, and the screen stays as it was.
|
|
63
|
+
*
|
|
64
|
+
* `showModal` finally acknowledges `interaction`: nothing can `.update()` on it
|
|
65
|
+
* again, which is why the submission carries its own interaction to answer
|
|
66
|
+
* through.
|
|
67
|
+
*
|
|
68
|
+
* `MessageComponentInteraction` rather than `AnySelectMenuInteraction`: a menu
|
|
69
|
+
* entry opens the very same modal whether it is reached by picking it from a
|
|
70
|
+
* select (the embeds layout) or by clicking the button its own `Section`
|
|
71
|
+
* carries (the card layout), and both interaction kinds carry `showModal` and
|
|
72
|
+
* `awaitModalSubmit` from that shared base.
|
|
73
|
+
*/
|
|
74
|
+
async function promptEditorFieldValue(interaction, field, current, translator, locale) {
|
|
75
|
+
const modal = (0, settings_editor_fields_1.isPickerField)(field)
|
|
76
|
+
? createPickerModal(field, current, translator, locale)
|
|
77
|
+
: field.kind === "image"
|
|
78
|
+
? createImageModal(field, current, translator, locale)
|
|
79
|
+
: createTextModal(field, current, translator, locale);
|
|
80
|
+
try {
|
|
81
|
+
const built = modal.build(nextModalOpenState(field.key));
|
|
82
|
+
const expectedCustomId = customIdOf(built);
|
|
83
|
+
await interaction.showModal(built);
|
|
84
|
+
const submitted = await interaction.awaitModalSubmit({
|
|
85
|
+
time: MODAL_TIMEOUT_MS,
|
|
86
|
+
filter: (submission) => submission.user.id === interaction.user.id && submission.customId === expectedCustomId,
|
|
87
|
+
});
|
|
88
|
+
// A modal opened from a message submits as a `ModalMessageModalSubmit`
|
|
89
|
+
// interaction — the only kind that can `.update()` the screen in place.
|
|
90
|
+
// Anything else did not come from it, so it is dropped rather than answered
|
|
91
|
+
// on a message it has no claim to.
|
|
92
|
+
if (!submitted.isFromMessage()) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
return { interaction: submitted, ...modal.read(submitted) };
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Ask for every setting a group declares at once, in a single modal — the
|
|
103
|
+
* group's counterpart of {@link promptEditorFieldValue}. Each member is laid
|
|
104
|
+
* out in the geometry its own `kind` imposes, exactly as it would if it were
|
|
105
|
+
* prompted on its own, and prefilled the same way: from `currentValue`, asked
|
|
106
|
+
* once per member.
|
|
107
|
+
*
|
|
108
|
+
* `legend` is extra text shown above the fields — a token list a message field
|
|
109
|
+
* reads against, say — already counted against the modal's own five-component
|
|
110
|
+
* budget by the caller that built it; `null` when the group has nothing to say
|
|
111
|
+
* there.
|
|
112
|
+
*/
|
|
113
|
+
async function promptEditorGroupValue(interaction, group, currentValue, legend, translator, locale) {
|
|
114
|
+
const modal = createGroupModal(group, currentValue, legend, translator, locale);
|
|
115
|
+
try {
|
|
116
|
+
const built = modal.build(nextModalOpenState(group.key));
|
|
117
|
+
const expectedCustomId = customIdOf(built);
|
|
118
|
+
await interaction.showModal(built);
|
|
119
|
+
const submitted = await interaction.awaitModalSubmit({
|
|
120
|
+
time: MODAL_TIMEOUT_MS,
|
|
121
|
+
filter: (submission) => submission.user.id === interaction.user.id && submission.customId === expectedCustomId,
|
|
122
|
+
});
|
|
123
|
+
if (!submitted.isFromMessage()) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
return { interaction: submitted, values: modal.read(submitted) };
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* A group's modal: one component per member field, each in the shape its own
|
|
134
|
+
* `kind` names, built through the very builders a single-field modal uses —
|
|
135
|
+
* `createModal`'s own generic `read` is what lets this stay one function
|
|
136
|
+
* whatever mix of text and picker members a group declares.
|
|
137
|
+
*
|
|
138
|
+
* A member of kind `image` is refused here rather than supported: an image
|
|
139
|
+
* field needs two components of its own (the upload, and the address beside
|
|
140
|
+
* it), which a group already spending one slot per member — and possibly one
|
|
141
|
+
* more on `legend` — has no general room for, and no group this repository
|
|
142
|
+
* declares needs one.
|
|
143
|
+
*/
|
|
144
|
+
function createGroupModal(group, currentValue, legend, translator, locale) {
|
|
145
|
+
const label = translator.translate(locale, group.labelKey);
|
|
146
|
+
const fields = {};
|
|
147
|
+
for (const member of group.fields) {
|
|
148
|
+
const current = currentValue(member.key);
|
|
149
|
+
if ((0, settings_editor_fields_1.isPickerField)(member)) {
|
|
150
|
+
fields[member.key] = pickerFieldDef(member, pickerShared(member, translator, locale), current.picked, translator, locale);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (member.kind === "image") {
|
|
154
|
+
throw new Error(`Settings editor group "${group.key}" declares member "${member.key}" of kind "image"; a group's modal has no room for the separate upload component an image field needs. Declare it as "text" instead.`);
|
|
155
|
+
}
|
|
156
|
+
fields[member.key] = valueFieldDefOf(member, current, translator, locale);
|
|
157
|
+
}
|
|
158
|
+
const modal = (0, modal_1.createModal)({
|
|
159
|
+
id: EDITOR_GROUP_MODAL_ID,
|
|
160
|
+
title: modalTitle(label, translator, locale),
|
|
161
|
+
...(legend === null ? {} : { legend }),
|
|
162
|
+
fields,
|
|
163
|
+
});
|
|
164
|
+
return {
|
|
165
|
+
build: (state) => modal.build(state),
|
|
166
|
+
read: (interaction) => {
|
|
167
|
+
const raw = modal.read(interaction);
|
|
168
|
+
const values = {};
|
|
169
|
+
for (const member of group.fields) {
|
|
170
|
+
values[member.key] = (0, settings_editor_fields_1.isPickerField)(member)
|
|
171
|
+
? { value: null, upload: null, ids: raw[member.key] }
|
|
172
|
+
: { value: raw[member.key], upload: null, ids: null };
|
|
173
|
+
}
|
|
174
|
+
return values;
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function modalTitle(label, translator, locale) {
|
|
179
|
+
return translator.translate(locale, i18n_1.SETTINGS_EDITOR_MESSAGES.modalTitle, { field: label });
|
|
180
|
+
}
|
|
181
|
+
/** The greyed-out example inside the box, for a field that declared one. */
|
|
182
|
+
function placeholderOf(field, translator, locale) {
|
|
183
|
+
if (field.placeholderKey === undefined) {
|
|
184
|
+
return {};
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
placeholder: translator.translate(locale, field.placeholderKey, field.placeholderParams),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* The one text-input component a `text`-kind field builds, whether it is
|
|
192
|
+
* prompted on its own (via {@link createTextModal}) or as a member of a group
|
|
193
|
+
* — the shape a group's modal needs from `createModal`'s own generic `read`
|
|
194
|
+
* is exactly this, unlabelled by which caller asked for it.
|
|
195
|
+
*/
|
|
196
|
+
function valueFieldDefOf(field, current, translator, locale) {
|
|
197
|
+
return {
|
|
198
|
+
kind: "text",
|
|
199
|
+
label: translator.translate(locale, field.labelKey),
|
|
200
|
+
description: translator.translate(locale, field.helperKey ?? i18n_1.SETTINGS_EDITOR_MESSAGES.modalHelper),
|
|
201
|
+
style: field.style,
|
|
202
|
+
required: false,
|
|
203
|
+
maxLength: field.maxLength,
|
|
204
|
+
...placeholderOf(field, translator, locale),
|
|
205
|
+
...(current.text === null ? {} : { value: current.text }),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/** Every field but the images: one optional text input, emptied to clear. */
|
|
209
|
+
function createTextModal(field, current, translator, locale) {
|
|
210
|
+
const label = translator.translate(locale, field.labelKey);
|
|
211
|
+
const modal = (0, modal_1.createModal)({
|
|
212
|
+
id: EDITOR_FIELD_MODAL_ID,
|
|
213
|
+
title: modalTitle(label, translator, locale),
|
|
214
|
+
fields: { value: valueFieldDefOf(field, current, translator, locale) },
|
|
215
|
+
});
|
|
216
|
+
return {
|
|
217
|
+
build: (state) => modal.build(state),
|
|
218
|
+
read: (interaction) => ({ value: modal.read(interaction).value, upload: null, ids: null }),
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* An image field: a file upload and the address input side by side, both
|
|
223
|
+
* optional, two of the five components a modal may carry.
|
|
224
|
+
*
|
|
225
|
+
* Offering both is what keeps an administrator from having to host the file
|
|
226
|
+
* somewhere first, without taking away the address that a guild already
|
|
227
|
+
* pointing at its own CDN relies on.
|
|
228
|
+
*/
|
|
229
|
+
function createImageModal(field, current, translator, locale) {
|
|
230
|
+
const label = translator.translate(locale, field.labelKey);
|
|
231
|
+
const modal = (0, modal_1.createModal)({
|
|
232
|
+
id: EDITOR_FIELD_MODAL_ID,
|
|
233
|
+
title: modalTitle(label, translator, locale),
|
|
234
|
+
fields: {
|
|
235
|
+
upload: {
|
|
236
|
+
kind: "file",
|
|
237
|
+
label: translator.translate(locale, i18n_1.SETTINGS_EDITOR_MESSAGES.modalUploadLabel),
|
|
238
|
+
description: translator.translate(locale, i18n_1.SETTINGS_EDITOR_MESSAGES.modalUploadHelper, {
|
|
239
|
+
max: field.uploadMaxMib ?? 0,
|
|
240
|
+
}),
|
|
241
|
+
required: false,
|
|
242
|
+
maxValues: MAX_UPLOADS,
|
|
243
|
+
},
|
|
244
|
+
value: {
|
|
245
|
+
kind: "text",
|
|
246
|
+
label: translator.translate(locale, i18n_1.SETTINGS_EDITOR_MESSAGES.modalImageUrlLabel),
|
|
247
|
+
description: translator.translate(locale, current.uploaded
|
|
248
|
+
? i18n_1.SETTINGS_EDITOR_MESSAGES.modalImageHelperUploaded
|
|
249
|
+
: i18n_1.SETTINGS_EDITOR_MESSAGES.modalImageHelper),
|
|
250
|
+
style: field.style,
|
|
251
|
+
required: false,
|
|
252
|
+
maxLength: field.maxLength,
|
|
253
|
+
...(current.text === null ? {} : { value: current.text }),
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
return {
|
|
258
|
+
build: (state) => modal.build(state),
|
|
259
|
+
read: (interaction) => {
|
|
260
|
+
const values = modal.read(interaction);
|
|
261
|
+
return { value: values.value, upload: values.upload?.[0] ?? null, ids: null };
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* A picker field's modal: one select of the shape its kind names — role, user,
|
|
267
|
+
* channel or a closed set of choices — prefilled with what it already holds.
|
|
268
|
+
*
|
|
269
|
+
* `category` narrows a channel select to {@link ChannelType.GuildCategory}: the
|
|
270
|
+
* very shorthand `createPickerRow` used to express, reproduced here rather than
|
|
271
|
+
* left behind now that the picker lives in a modal instead of the message.
|
|
272
|
+
*/
|
|
273
|
+
function createPickerModal(field, current, translator, locale) {
|
|
274
|
+
const label = translator.translate(locale, field.labelKey);
|
|
275
|
+
const modal = (0, modal_1.createModal)({
|
|
276
|
+
id: EDITOR_FIELD_MODAL_ID,
|
|
277
|
+
title: modalTitle(label, translator, locale),
|
|
278
|
+
fields: {
|
|
279
|
+
value: pickerFieldDef(field, pickerShared(field, translator, locale), current.picked, translator, locale),
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
return {
|
|
283
|
+
build: (state) => modal.build(state),
|
|
284
|
+
read: (interaction) => {
|
|
285
|
+
const read = modal.read(interaction);
|
|
286
|
+
return { value: null, upload: null, ids: read.value };
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* What every picker component shares, whatever its own kind narrows it to —
|
|
292
|
+
* used whether the field is prompted on its own or as a member of a group.
|
|
293
|
+
*/
|
|
294
|
+
function pickerShared(field, translator, locale) {
|
|
295
|
+
const placeholder = field.pickerPlaceholderKey === undefined
|
|
296
|
+
? undefined
|
|
297
|
+
: translator.translate(locale, field.pickerPlaceholderKey);
|
|
298
|
+
return {
|
|
299
|
+
label: translator.translate(locale, field.labelKey),
|
|
300
|
+
// Zero is what lets the administrator clear the setting by emptying the
|
|
301
|
+
// select — `createModal` then hands `null` back on an empty submission
|
|
302
|
+
// instead of refusing to open with nothing picked.
|
|
303
|
+
required: (field.minValues ?? 1) !== 0,
|
|
304
|
+
minValues: field.minValues,
|
|
305
|
+
maxValues: field.maxValues,
|
|
306
|
+
...(placeholder === undefined ? {} : { placeholder }),
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
/** The one component a picker modal carries, in the shape its field's kind names. */
|
|
310
|
+
function pickerFieldDef(field, shared, held, translator, locale) {
|
|
311
|
+
// `modal.ts`'s builders open a default whenever the key is present at all,
|
|
312
|
+
// empty array included — so an empty selection is left out entirely rather
|
|
313
|
+
// than handed through, the same guard `createRoleSelect` (the select-menu
|
|
314
|
+
// DSL the in-message picker used) already applied.
|
|
315
|
+
switch (field.kind) {
|
|
316
|
+
case "role":
|
|
317
|
+
return { kind: "role", ...shared, ...(held.length === 0 ? {} : { defaultRoleIds: held }) };
|
|
318
|
+
case "user":
|
|
319
|
+
return { kind: "user", ...shared, ...(held.length === 0 ? {} : { defaultUserIds: held }) };
|
|
320
|
+
case "category":
|
|
321
|
+
return {
|
|
322
|
+
kind: "channel",
|
|
323
|
+
...shared,
|
|
324
|
+
channelTypes: [discord_js_1.ChannelType.GuildCategory],
|
|
325
|
+
...(held.length === 0 ? {} : { defaultChannelIds: held }),
|
|
326
|
+
};
|
|
327
|
+
case "channel":
|
|
328
|
+
return {
|
|
329
|
+
kind: "channel",
|
|
330
|
+
...shared,
|
|
331
|
+
channelTypes: field.channelTypes,
|
|
332
|
+
...(held.length === 0 ? {} : { defaultChannelIds: held }),
|
|
333
|
+
};
|
|
334
|
+
case "choice":
|
|
335
|
+
return {
|
|
336
|
+
kind: "select",
|
|
337
|
+
...shared,
|
|
338
|
+
options: field.choices.map((choice) => ({
|
|
339
|
+
label: translator.translate(locale, choice.labelKey),
|
|
340
|
+
value: choice.value,
|
|
341
|
+
...(choice.descriptionKey === undefined
|
|
342
|
+
? {}
|
|
343
|
+
: { description: translator.translate(locale, choice.descriptionKey) }),
|
|
344
|
+
// Marked rather than prefilled the way an entity select's default is:
|
|
345
|
+
// Discord has no "default value" concept for a string select, only a
|
|
346
|
+
// per-option flag, which is also how the reader still sees which of a
|
|
347
|
+
// closed set is in force.
|
|
348
|
+
default: held.includes(choice.value),
|
|
349
|
+
})),
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
//# sourceMappingURL=settings-editor-field-modal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings-editor-field-modal.js","sourceRoot":"","sources":["../../../../src/discord/components/settings-editor/settings-editor-field-modal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import type { ChannelType } from "discord.js";
|
|
2
|
+
import type { TextFieldStyleName } from "../../../discord/interaction/modal";
|
|
3
|
+
import type { TranslationParams } from "../../../i18n";
|
|
4
|
+
/**
|
|
5
|
+
* How a field is entered.
|
|
6
|
+
*
|
|
7
|
+
* `image` is the only typed one that differs: it offers a file upload beside the
|
|
8
|
+
* address box, because an administrator should not have to host a picture
|
|
9
|
+
* somewhere before the bot will show it.
|
|
10
|
+
*
|
|
11
|
+
* `role`, `category`, `user`, `channel` and `choice` are picked rather than
|
|
12
|
+
* typed — see {@link SettingsEditorPickerField}.
|
|
13
|
+
*/
|
|
14
|
+
export type SettingsEditorFieldKind = "text" | "image" | "level" | "role" | "category" | "user" | "channel" | "choice";
|
|
15
|
+
/**
|
|
16
|
+
* What every menu entry states, whatever picking it does.
|
|
17
|
+
*
|
|
18
|
+
* `hintKey` is required rather than optional on purpose: an entry the reader
|
|
19
|
+
* cannot tell apart from its neighbours is the very problem these screens exist
|
|
20
|
+
* to solve, so a declaration without one does not typecheck.
|
|
21
|
+
*/
|
|
22
|
+
interface SettingsEditorEntry<F extends string> {
|
|
23
|
+
readonly key: F;
|
|
24
|
+
readonly labelKey: string;
|
|
25
|
+
/** Shown under the label in the menu — what this field controls, in one line. */
|
|
26
|
+
readonly hintKey: string;
|
|
27
|
+
/**
|
|
28
|
+
* The emoji a card layout's own button wears for this entry, in place of the
|
|
29
|
+
* generic one `mount-settings-editor.ts` would otherwise pick from the
|
|
30
|
+
* entry's own kind — a pencil for something edited, an arrow for a level
|
|
31
|
+
* descended into. Declared only where the generic would lie: an entry that
|
|
32
|
+
* opens a modal to *create* something reads wrong under a pencil, the icon
|
|
33
|
+
* for changing what already exists. Left undeclared, which is most entries,
|
|
34
|
+
* the generic stands exactly as it did before this existed.
|
|
35
|
+
*/
|
|
36
|
+
readonly icon?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* An entry that goes somewhere rather than editing anything: picking it descends
|
|
40
|
+
* into `levelKey`, and the return control comes back.
|
|
41
|
+
*
|
|
42
|
+
* It declares no modal geometry because it opens no modal — which the union
|
|
43
|
+
* below enforces rather than leaving to a comment.
|
|
44
|
+
*/
|
|
45
|
+
export interface SettingsEditorLevelField<F extends string> extends SettingsEditorEntry<F> {
|
|
46
|
+
readonly kind: "level";
|
|
47
|
+
readonly levelKey: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* One editable field of a customisable embed: what it is called, what editing it
|
|
51
|
+
* looks like, and what it is allowed to hold.
|
|
52
|
+
*
|
|
53
|
+
* Pure data, deliberately: both screens declare their fields as a constant list,
|
|
54
|
+
* which is what lets the menu, the modal and the notices be built from one
|
|
55
|
+
* declaration instead of three that have to agree.
|
|
56
|
+
*/
|
|
57
|
+
export interface SettingsEditorValueField<F extends string> extends SettingsEditorEntry<F> {
|
|
58
|
+
readonly kind: "text" | "image";
|
|
59
|
+
readonly style: TextFieldStyleName;
|
|
60
|
+
/**
|
|
61
|
+
* Stops an over-long value at the client instead of rejecting it after the
|
|
62
|
+
* round trip. The domain parsers still enforce the real limit: this one is
|
|
63
|
+
* capped by what a modal input can carry at all.
|
|
64
|
+
*/
|
|
65
|
+
readonly maxLength: number;
|
|
66
|
+
/**
|
|
67
|
+
* Replaces the generic "leave empty to clear" helper under the box, for a
|
|
68
|
+
* field whose empty case means something of its own.
|
|
69
|
+
*/
|
|
70
|
+
readonly helperKey?: string;
|
|
71
|
+
/**
|
|
72
|
+
* The upload cap the helper text states, in MiB. Declared by the screen
|
|
73
|
+
* because the limit is the caller's own — the editor neither enforces it nor
|
|
74
|
+
* knows what is being stored.
|
|
75
|
+
*/
|
|
76
|
+
readonly uploadMaxMib?: number;
|
|
77
|
+
/** Greyed-out example inside the box — the welcome message's token list. */
|
|
78
|
+
readonly placeholderKey?: string;
|
|
79
|
+
readonly placeholderParams?: TranslationParams;
|
|
80
|
+
}
|
|
81
|
+
/** What every picked entry states, whichever list it is picked from. */
|
|
82
|
+
interface SettingsEditorPickerEntry<F extends string> extends SettingsEditorEntry<F> {
|
|
83
|
+
/** How many may be picked at once. One unless the screen says otherwise. */
|
|
84
|
+
readonly maxValues?: number;
|
|
85
|
+
/** Zero lets the picker clear the setting; one makes it mandatory. */
|
|
86
|
+
readonly minValues?: number;
|
|
87
|
+
/** Shown inside the picker before anything is chosen. */
|
|
88
|
+
readonly pickerPlaceholderKey?: string;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* An entry picked from a list Discord populates itself — a role, a member, or a
|
|
92
|
+
* category channel.
|
|
93
|
+
*
|
|
94
|
+
* `category` is the shorthand for the one channel shape a screen keeps asking
|
|
95
|
+
* for; anything else states its own types through
|
|
96
|
+
* {@link SettingsEditorChannelField}.
|
|
97
|
+
*/
|
|
98
|
+
export interface SettingsEditorEntityField<F extends string> extends SettingsEditorPickerEntry<F> {
|
|
99
|
+
readonly kind: "role" | "category" | "user";
|
|
100
|
+
}
|
|
101
|
+
/** An entry picked from the guild's channels, narrowed to the types it names. */
|
|
102
|
+
export interface SettingsEditorChannelField<F extends string> extends SettingsEditorPickerEntry<F> {
|
|
103
|
+
readonly kind: "channel";
|
|
104
|
+
/**
|
|
105
|
+
* Required rather than optional: a channel picker offering every kind in the
|
|
106
|
+
* guild for a setting that only accepts one of them invites a choice the write
|
|
107
|
+
* then refuses.
|
|
108
|
+
*/
|
|
109
|
+
readonly channelTypes: readonly ChannelType[];
|
|
110
|
+
}
|
|
111
|
+
/** One option of a {@link SettingsEditorChoiceField}. */
|
|
112
|
+
export interface SettingsEditorChoice {
|
|
113
|
+
/** What is stored — never shown, so it is not translated. */
|
|
114
|
+
readonly value: string;
|
|
115
|
+
readonly labelKey: string;
|
|
116
|
+
/** Shown under the label, as a menu entry's hint is. Optional: a two-way toggle rarely needs one. */
|
|
117
|
+
readonly descriptionKey?: string;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* An entry picked from a list the screen itself declares — a visibility mode, a
|
|
121
|
+
* weekday.
|
|
122
|
+
*
|
|
123
|
+
* The command layer expresses these as `createStringOption().choices(...)`, and
|
|
124
|
+
* a setting that had a closed set of values when it was a command still has one
|
|
125
|
+
* once it moves behind a screen. Typing the value back into a modal would turn a
|
|
126
|
+
* guaranteed-valid pick into a spelling test whose only feedback is a refusal.
|
|
127
|
+
*/
|
|
128
|
+
export interface SettingsEditorChoiceField<F extends string> extends SettingsEditorPickerEntry<F> {
|
|
129
|
+
readonly kind: "choice";
|
|
130
|
+
readonly choices: readonly SettingsEditorChoice[];
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* An entry whose value is picked rather than typed.
|
|
134
|
+
*
|
|
135
|
+
* A modal carries text inputs only and cannot be changed once open, so a role, a
|
|
136
|
+
* member, a channel or one of a closed set of values has to be chosen from a
|
|
137
|
+
* component. Declaring the kind rather than wiring each picker by hand keeps the
|
|
138
|
+
* one-declaration property: a field cannot appear in the menu with no way to
|
|
139
|
+
* edit it.
|
|
140
|
+
*
|
|
141
|
+
* It states no modal geometry, for the same reason a level does not.
|
|
142
|
+
*/
|
|
143
|
+
export type SettingsEditorPickerField<F extends string> = SettingsEditorEntityField<F> | SettingsEditorChannelField<F> | SettingsEditorChoiceField<F>;
|
|
144
|
+
/**
|
|
145
|
+
* An entry that asks for several settings at once, in a single modal — a
|
|
146
|
+
* tournament's announcement message, colour, image and footer, which read as
|
|
147
|
+
* one decision rather than four.
|
|
148
|
+
*
|
|
149
|
+
* Its member fields are declared *inside* the group rather than as menu
|
|
150
|
+
* entries of their own: that is what keeps the repository's "one declaration"
|
|
151
|
+
* property — each member still owns the single `key` `currentValue` and the
|
|
152
|
+
* write are named by, it is only the menu that no longer lists it separately.
|
|
153
|
+
*
|
|
154
|
+
* A level that reduces to a single group is not a level: per the rule this
|
|
155
|
+
* repository already states for `COMMS_AREA_FIELDS` ("a domain that carries a
|
|
156
|
+
* single setting *is* that setting, not a level"), a group replaces the level
|
|
157
|
+
* outright rather than being offered as its lone entry.
|
|
158
|
+
*/
|
|
159
|
+
export interface SettingsEditorGroupField<F extends string> extends SettingsEditorEntry<F> {
|
|
160
|
+
readonly kind: "group";
|
|
161
|
+
/** The settings this modal asks for, in the order it poses them. */
|
|
162
|
+
readonly fields: readonly (SettingsEditorValueField<F> | SettingsEditorPickerField<F>)[];
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* A menu entry: either something to edit, or somewhere to go.
|
|
166
|
+
*
|
|
167
|
+
* Kept as one union so a level and a field are declared side by side in the same
|
|
168
|
+
* list, in the order the menu offers them — the screen's shape is one constant,
|
|
169
|
+
* not a field list plus a navigation table that could disagree about ordering.
|
|
170
|
+
*/
|
|
171
|
+
export type SettingsEditorField<F extends string> = SettingsEditorValueField<F> | SettingsEditorPickerField<F> | SettingsEditorLevelField<F> | SettingsEditorGroupField<F>;
|
|
172
|
+
/** Whether this entry is typed — a text box or an image upload — rather than picked. */
|
|
173
|
+
export declare function isValueField<F extends string>(field: SettingsEditorField<F>): field is SettingsEditorValueField<F>;
|
|
174
|
+
/**
|
|
175
|
+
* Whether this entry is picked from a list rather than typed.
|
|
176
|
+
*
|
|
177
|
+
* Both shapes open a modal; this only decides which geometry it opens with — a
|
|
178
|
+
* role/user/channel select or a string select, prefilled from the current
|
|
179
|
+
* selection, in place of a text input.
|
|
180
|
+
*/
|
|
181
|
+
export declare function isPickerField<F extends string>(field: SettingsEditorField<F>): field is SettingsEditorPickerField<F>;
|
|
182
|
+
/** Whether this entry opens a modal asking for several settings at once. */
|
|
183
|
+
export declare function isGroupField<F extends string>(field: SettingsEditorField<F>): field is SettingsEditorGroupField<F>;
|
|
184
|
+
/** The stored value of one field, as the modal editing it opens on. */
|
|
185
|
+
export interface SettingsEditorFieldValue {
|
|
186
|
+
/** Prefills the text input; `null` when there is nothing to type back. */
|
|
187
|
+
readonly text: string | null;
|
|
188
|
+
/**
|
|
189
|
+
* Whether the field currently holds an upload. Discord cannot prefill a file
|
|
190
|
+
* input, so the modal says so in words instead of opening on a blank that
|
|
191
|
+
* reads like nothing being set.
|
|
192
|
+
*/
|
|
193
|
+
readonly uploaded: boolean;
|
|
194
|
+
/** The identifiers a picker field currently carries. Empty for a field that is not picked. */
|
|
195
|
+
readonly picked: readonly string[];
|
|
196
|
+
}
|
|
197
|
+
/** A text override, as the modal editing it opens on. */
|
|
198
|
+
export declare function textFieldValue(value: string | null): SettingsEditorFieldValue;
|
|
199
|
+
/** A picker override, as the modal editing it opens on — prefilled from what it already holds. */
|
|
200
|
+
export declare function pickedFieldValue(ids: readonly string[]): SettingsEditorFieldValue;
|
|
201
|
+
/** Discord's own cap on a modal text input, whatever the embed itself would take. */
|
|
202
|
+
export declare const MAX_MODAL_INPUT_LENGTH = 4000;
|
|
203
|
+
/**
|
|
204
|
+
* Discord's own cap on a modal's top-level components: "between 1 and 5
|
|
205
|
+
* (inclusive) components that make up the modal" (Discord's interaction
|
|
206
|
+
* callback data reference). A group's member fields count against it one for
|
|
207
|
+
* one — each becomes a `Label` — and so does the legend a group's modal may
|
|
208
|
+
* carry, via a `TextDisplay`.
|
|
209
|
+
*/
|
|
210
|
+
export declare const MAX_MODAL_COMPONENTS = 5;
|
|
211
|
+
/** Whether a value picked from the menu is one of the fields a screen declared. */
|
|
212
|
+
export declare function isEditorField<F extends string>(fields: readonly SettingsEditorField<F>[], value: string): value is F;
|
|
213
|
+
/** The declaration of one field, by key. */
|
|
214
|
+
export declare function editorField<F extends string>(fields: readonly SettingsEditorField<F>[], key: F): SettingsEditorField<F>;
|
|
215
|
+
export {};
|
|
216
|
+
//# sourceMappingURL=settings-editor-fields.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings-editor-fields.d.ts","sourceRoot":"","sources":["../../../../src/discord/components/settings-editor/settings-editor-fields.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MAX_MODAL_COMPONENTS = exports.MAX_MODAL_INPUT_LENGTH = void 0;
|
|
4
|
+
exports.isValueField = isValueField;
|
|
5
|
+
exports.isPickerField = isPickerField;
|
|
6
|
+
exports.isGroupField = isGroupField;
|
|
7
|
+
exports.textFieldValue = textFieldValue;
|
|
8
|
+
exports.pickedFieldValue = pickedFieldValue;
|
|
9
|
+
exports.isEditorField = isEditorField;
|
|
10
|
+
exports.editorField = editorField;
|
|
11
|
+
/** Whether this entry is typed — a text box or an image upload — rather than picked. */
|
|
12
|
+
function isValueField(field) {
|
|
13
|
+
return field.kind === "text" || field.kind === "image";
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Whether this entry is picked from a list rather than typed.
|
|
17
|
+
*
|
|
18
|
+
* Both shapes open a modal; this only decides which geometry it opens with — a
|
|
19
|
+
* role/user/channel select or a string select, prefilled from the current
|
|
20
|
+
* selection, in place of a text input.
|
|
21
|
+
*/
|
|
22
|
+
function isPickerField(field) {
|
|
23
|
+
return (field.kind === "role" ||
|
|
24
|
+
field.kind === "category" ||
|
|
25
|
+
field.kind === "user" ||
|
|
26
|
+
field.kind === "channel" ||
|
|
27
|
+
field.kind === "choice");
|
|
28
|
+
}
|
|
29
|
+
/** Whether this entry opens a modal asking for several settings at once. */
|
|
30
|
+
function isGroupField(field) {
|
|
31
|
+
return field.kind === "group";
|
|
32
|
+
}
|
|
33
|
+
/** A text override, as the modal editing it opens on. */
|
|
34
|
+
function textFieldValue(value) {
|
|
35
|
+
return { text: value, uploaded: false, picked: [] };
|
|
36
|
+
}
|
|
37
|
+
/** A picker override, as the modal editing it opens on — prefilled from what it already holds. */
|
|
38
|
+
function pickedFieldValue(ids) {
|
|
39
|
+
return { text: null, uploaded: false, picked: ids };
|
|
40
|
+
}
|
|
41
|
+
/** Discord's own cap on a modal text input, whatever the embed itself would take. */
|
|
42
|
+
exports.MAX_MODAL_INPUT_LENGTH = 4000;
|
|
43
|
+
/**
|
|
44
|
+
* Discord's own cap on a modal's top-level components: "between 1 and 5
|
|
45
|
+
* (inclusive) components that make up the modal" (Discord's interaction
|
|
46
|
+
* callback data reference). A group's member fields count against it one for
|
|
47
|
+
* one — each becomes a `Label` — and so does the legend a group's modal may
|
|
48
|
+
* carry, via a `TextDisplay`.
|
|
49
|
+
*/
|
|
50
|
+
exports.MAX_MODAL_COMPONENTS = 5;
|
|
51
|
+
/** Whether a value picked from the menu is one of the fields a screen declared. */
|
|
52
|
+
function isEditorField(fields, value) {
|
|
53
|
+
return fields.some((field) => field.key === value);
|
|
54
|
+
}
|
|
55
|
+
/** The declaration of one field, by key. */
|
|
56
|
+
function editorField(fields, key) {
|
|
57
|
+
const found = fields.find((field) => field.key === key);
|
|
58
|
+
if (found === undefined) {
|
|
59
|
+
// Unreachable: a key only ever reaches here after `isEditorField` matched
|
|
60
|
+
// it against this very list. Kept because nothing in the types ties the two
|
|
61
|
+
// calls together.
|
|
62
|
+
throw new Error(`No editable field declared under "${key}"`);
|
|
63
|
+
}
|
|
64
|
+
return found;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=settings-editor-fields.js.map
|