Using a React modal for the YesNo prompt and ensured that only string will be passed
parent
5ef2d85d6f
commit
a414caef42
|
@ -7,7 +7,6 @@ import {LogCategory, logDebug, logError, logInfo, logWarn} from "tc-shared/log";
|
|||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||
import {createErrorModal, createInfoModal} from "tc-shared/ui/elements/Modal";
|
||||
import {RecorderProfile, setDefaultRecorder} from "tc-shared/voice/RecorderProfile";
|
||||
import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo";
|
||||
import {formatMessage} from "tc-shared/ui/frames/chat";
|
||||
import {openModalNewcomer} from "tc-shared/ui/modal/ModalNewcomer";
|
||||
import {global_client_actions} from "tc-shared/events/GlobalEvents";
|
||||
|
@ -53,6 +52,7 @@ import "./text/bbcode/InviteController";
|
|||
import "./text/bbcode/YoutubeController";
|
||||
import {initializeSounds, setSoundMasterVolume} from "./audio/Sounds";
|
||||
import {getInstanceConnectHandler, setupIpcHandler} from "./ipc/BrowserIPC";
|
||||
import {promptYesNo} from "tc-shared/ui/modal/yes-no/Controller";
|
||||
|
||||
assertMainApplication();
|
||||
|
||||
|
@ -203,11 +203,12 @@ async function doHandleConnectRequest(serverAddress: string, serverUniqueId: str
|
|||
|
||||
if(!getAudioBackend().isInitialized()) {
|
||||
/* Trick the client into clicking somewhere on the site to initialize audio */
|
||||
const resultPromise = new Promise<boolean>(resolve => {
|
||||
spawnYesNo(tra("Connect to {}", serverAddress), tra("Would you like to connect to {}?", serverAddress), resolve).open();
|
||||
const result = await promptYesNo({
|
||||
title: tra("Connect to {}", serverAddress),
|
||||
question: tra("Would you like to connect to {}?", serverAddress)
|
||||
});
|
||||
|
||||
if(!(await resultPromise)) {
|
||||
if(!result) {
|
||||
/* Well... the client don't want to... */
|
||||
return { status: "client-aborted" };
|
||||
}
|
||||
|
@ -441,13 +442,12 @@ const task_connect_handler: loader.Task = {
|
|||
const chandler = getInstanceConnectHandler();
|
||||
if(chandler && AppParameters.getValue(AppParameters.KEY_CONNECT_NO_SINGLE_INSTANCE)) {
|
||||
try {
|
||||
await chandler.post_connect_request(connectData, () => new Promise<boolean>(resolve => {
|
||||
spawnYesNo(tr("Another TeaWeb instance is already running"), tra("Another TeaWeb instance is already running.\nWould you like to connect there?"), response => {
|
||||
resolve(response);
|
||||
}, {
|
||||
closeable: false
|
||||
}).open();
|
||||
}));
|
||||
await chandler.post_connect_request(connectData, async () => {
|
||||
return await promptYesNo({
|
||||
title: tr("Another TeaWeb instance is already running"),
|
||||
question: tra("Another TeaWeb instance is already running.\nWould you like to connect there?")
|
||||
});
|
||||
});
|
||||
logInfo(LogCategory.CLIENT, tr("Executed connect successfully in another browser window. Closing this window"));
|
||||
|
||||
createInfoModal(
|
||||
|
|
|
@ -10,13 +10,10 @@ import {ClientEntry, LocalClientEntry, MusicClientEntry} from "./Client";
|
|||
import {ChannelTreeEntry} from "./ChannelTreeEntry";
|
||||
import {ConnectionHandler, ViewReasonId} from "tc-shared/ConnectionHandler";
|
||||
import {Registry} from "tc-shared/events";
|
||||
import * as React from "react";
|
||||
|
||||
import {batch_updates, BatchUpdateType, flush_batched_updates} from "tc-shared/ui/react-elements/ReactComponentBase";
|
||||
import {createInputModal} from "tc-shared/ui/elements/Modal";
|
||||
import {spawnBanClient} from "tc-shared/ui/modal/ModalBanClient";
|
||||
import {formatMessage} from "tc-shared/ui/frames/chat";
|
||||
import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo";
|
||||
import {formatMessageString} from "tc-shared/ui/frames/chat";
|
||||
import {tr, tra} from "tc-shared/i18n/localize";
|
||||
import {initializeChannelTreeUiEvents} from "tc-shared/ui/tree/Controller";
|
||||
import {ChannelTreePopoutController} from "tc-shared/ui/tree/popout/Controller";
|
||||
|
@ -26,6 +23,7 @@ import {ClientIcon} from "svg-sprites/client-icons";
|
|||
import "./EntryTagsHandler";
|
||||
import {spawnChannelEditNew} from "tc-shared/ui/modal/channel-edit/Controller";
|
||||
import {ChannelTreeUIEvents} from "tc-shared/ui/tree/Definitions";
|
||||
import {promptYesNo} from "tc-shared/ui/modal/yes-no/Controller";
|
||||
|
||||
export interface ChannelTreeEvents {
|
||||
/* general tree notified */
|
||||
|
@ -694,17 +692,20 @@ export class ChannelTree {
|
|||
disabled: false,
|
||||
callback: () => {
|
||||
const param_string = clients.map((_, index) => "{" + index + "}").join(', ');
|
||||
const param_values = clients.map(client => client.createChatTag(true));
|
||||
const tag = $.spawn("div").append(...formatMessage(tr("Do you really want to delete ") + param_string, ...param_values));
|
||||
const tag_container = $.spawn("div").append(tag);
|
||||
spawnYesNo(tr("Are you sure?"), tag_container, result => {
|
||||
if(result) {
|
||||
const param_values = clients.map(client => client.clientNickName());
|
||||
promptYesNo({
|
||||
title: tr("Are you sure?"),
|
||||
question: formatMessageString(tr("Do you really want to delete ") + param_string, ...param_values)
|
||||
}).then(result => {
|
||||
if(!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(const client of clients) {
|
||||
this.client.serverConnection.send_command("musicbotdelete", {
|
||||
botid: client.properties.client_database_id
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
type: contextmenu.MenuEntryType.ENTRY
|
||||
|
@ -797,12 +798,17 @@ export class ChannelTree {
|
|||
name: tr("Delete all channels"),
|
||||
icon_class: "client-delete",
|
||||
callback: () => {
|
||||
spawnYesNo(tr("Are you sure?"), tra("Do you really want to delete {0} channels?", channels.length), result => {
|
||||
if(typeof result === "boolean" && result) {
|
||||
promptYesNo({
|
||||
title: tr("Are you sure?"),
|
||||
question: tra("Do you really want to delete {0} channels?", channels.length)
|
||||
}).then(result => {
|
||||
if(!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(const channel of channels) {
|
||||
this.client.serverConnection.send_command("channeldelete", { cid: channel.channelId });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -15,8 +15,6 @@ import {ConnectionHandler, ViewReasonId} from "../ConnectionHandler";
|
|||
import {openClientInfo} from "../ui/modal/ModalClientInfo";
|
||||
import {spawnBanClient} from "../ui/modal/ModalBanClient";
|
||||
import {spawnChangeLatency} from "../ui/modal/ModalChangeLatency";
|
||||
import {formatMessage} from "../ui/frames/chat";
|
||||
import {spawnYesNo} from "../ui/modal/ModalYesNo";
|
||||
import * as hex from "../crypto/hex";
|
||||
import {ChannelTreeEntry, ChannelTreeEntryEvents} from "./ChannelTreeEntry";
|
||||
import {spawnClientVolumeChange, spawnMusicBotVolumeChange} from "../ui/modal/ModalChangeVolumeNew";
|
||||
|
@ -27,10 +25,11 @@ import {VoiceClient} from "../voice/VoiceClient";
|
|||
import {VoicePlayerEvents, VoicePlayerState} from "../voice/VoicePlayer";
|
||||
import {ChannelTreeUIEvents} from "tc-shared/ui/tree/Definitions";
|
||||
import {VideoClient} from "tc-shared/connection/VideoConnection";
|
||||
import { tr } from "tc-shared/i18n/localize";
|
||||
import {tr, tra} from "tc-shared/i18n/localize";
|
||||
import {EventClient} from "tc-shared/connectionlog/Definitions";
|
||||
import {W2GPluginCmdHandler} from "tc-shared/ui/modal/video-viewer/W2GPlugin";
|
||||
import {spawnServerGroupAssignments} from "tc-shared/ui/modal/group-assignment/Controller";
|
||||
import {promptYesNo} from "tc-shared/ui/modal/yes-no/Controller";
|
||||
|
||||
export enum ClientType {
|
||||
CLIENT_VOICE,
|
||||
|
@ -1236,13 +1235,17 @@ export class MusicClientEntry extends ClientEntry<MusicClientEvents> {
|
|||
icon_class: "client-delete",
|
||||
disabled: false,
|
||||
callback: () => {
|
||||
const tag = $.spawn("div").append(formatMessage(tr("Do you really want to delete {0}"), this.createChatTag(false)));
|
||||
spawnYesNo(tr("Are you sure?"), $.spawn("div").append(tag), result => {
|
||||
if(result) {
|
||||
promptYesNo({
|
||||
title: tr("Are you sure?"),
|
||||
question: tra("Do you really want to delete {0}", this.clientNickName())
|
||||
}).then(result => {
|
||||
if(!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.channelTree.client.serverConnection.send_command("musicbotdelete", {
|
||||
bot_id: this.properties.client_database_id
|
||||
}).then(() => {});
|
||||
}
|
||||
});
|
||||
},
|
||||
type: contextmenu.MenuEntryType.ENTRY
|
||||
|
|
|
@ -2,10 +2,10 @@ import {createErrorModal, createModal} from "../../ui/elements/Modal";
|
|||
import {LogCategory, logError} from "../../log";
|
||||
import {ConnectionHandler} from "../../ConnectionHandler";
|
||||
import {base64_encode_ab} from "../../utils/buffers";
|
||||
import {spawnYesNo} from "../../ui/modal/ModalYesNo";
|
||||
import {ClientEntry} from "../../tree/Client";
|
||||
import moment from "moment";
|
||||
import {tr} from "tc-shared/i18n/localize";
|
||||
import {promptYesNo} from "tc-shared/ui/modal/yes-no/Controller";
|
||||
|
||||
const avatar_to_uid = (id: string) => {
|
||||
const buffer = new Uint8Array(id.length / 2);
|
||||
|
@ -90,7 +90,10 @@ export function spawnAvatarList(client: ConnectionHandler) {
|
|||
*/
|
||||
|
||||
callback_delete = () => {
|
||||
spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this avatar?"), result => {
|
||||
promptYesNo({
|
||||
title: tr("Are you sure?"),
|
||||
question: tr("Do you really want to delete this avatar?"),
|
||||
}).then(result => {
|
||||
if (result) {
|
||||
createErrorModal(tr("Not implemented"), tr("Avatar delete hasn't implemented yet")).open();
|
||||
//TODO Implement avatar delete
|
||||
|
|
|
@ -2,12 +2,12 @@ import {createModal, Modal} from "tc-shared/ui/elements/Modal";
|
|||
import {tra} from "tc-shared/i18n/localize";
|
||||
import {Registry} from "tc-shared/events";
|
||||
import {modal_settings, SettingProfileEvents} from "tc-shared/ui/modal/ModalSettings";
|
||||
import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo";
|
||||
import {initialize_audio_microphone_controller} from "tc-shared/ui/modal/settings/Microphone";
|
||||
import {MicrophoneSettings} from "tc-shared/ui/modal/settings/MicrophoneRenderer";
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import {MicrophoneSettingsEvents} from "tc-shared/ui/modal/settings/MicrophoneDefinitions";
|
||||
import {promptYesNo} from "tc-shared/ui/modal/yes-no/Controller";
|
||||
|
||||
export interface EventModalNewcomer {
|
||||
"show_step": {
|
||||
|
@ -65,7 +65,10 @@ export function openModalNewcomer(): Modal {
|
|||
|
||||
event_registry.on("exit_guide", event => {
|
||||
if (event.ask_yesno) {
|
||||
spawnYesNo(tr("Are you sure?"), tr("Do you really want to skip the basic setup guide?"), result => {
|
||||
promptYesNo({
|
||||
title: tr("Are you sure?"),
|
||||
question: tr("Do you really want to skip the basic setup guide?"),
|
||||
}).then(result => {
|
||||
if (result)
|
||||
event_registry.fire("exit_guide", {ask_yesno: false});
|
||||
});
|
||||
|
|
|
@ -116,7 +116,10 @@ export function spawnQueryManage(client: ConnectionHandler) {
|
|||
template.find(".button-query-delete").on('click', () => {
|
||||
if(!selected_query) return;
|
||||
|
||||
Modals.spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this account?"), result => {
|
||||
Modals.promptYesNo({
|
||||
title: tr("Are you sure?"),
|
||||
question: tr("Do you really want to delete this account?"),
|
||||
}).then(result => {
|
||||
if(result) {
|
||||
client.serverConnection.send_command("querydelete", {
|
||||
client_login_name: selected_query.username
|
||||
|
@ -161,7 +164,6 @@ import {createErrorModal, createInfoModal, createInputModal, createModal, Modal}
|
|||
import {CommandResult, QueryListEntry} from "../../connection/ServerConnectionDeclaration";
|
||||
import {SingleCommandHandler} from "../../connection/ConnectionBase";
|
||||
import {copyToClipboard} from "../../utils/helpers";
|
||||
import {spawnYesNo} from "../../ui/modal/ModalYesNo";
|
||||
import {LogCategory, logError} from "../../log";
|
||||
import PermissionType from "../../permission/PermissionType";
|
||||
import {ConnectionHandler} from "../../ConnectionHandler";
|
||||
|
@ -169,6 +171,7 @@ import {spawnQueryCreate, spawnQueryCreated} from "../../ui/modal/ModalQuery";
|
|||
import {formatMessage} from "../../ui/frames/chat";
|
||||
import {ErrorCode} from "../../connection/ErrorCode";
|
||||
import {tr} from "tc-shared/i18n/localize";
|
||||
import {promptYesNo} from "tc-shared/ui/modal/yes-no/Controller";
|
||||
|
||||
export function spawnQueryManage(client: ConnectionHandler) {
|
||||
let modal: Modal;
|
||||
|
@ -336,7 +339,10 @@ export function spawnQueryManage(client: ConnectionHandler) {
|
|||
button_delete.on('click', event => {
|
||||
if (!selected_query) return;
|
||||
|
||||
spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this account?"), result => {
|
||||
promptYesNo({
|
||||
title: tr("Are you sure?"),
|
||||
question: tr("Do you really want to delete this account?"),
|
||||
}).then(result => {
|
||||
if (result) {
|
||||
client.serverConnection.send_command("querydelete", {
|
||||
client_login_name: selected_query.username
|
||||
|
|
|
@ -13,7 +13,6 @@ import {LogCategory, logDebug, logError, logTrace, logWarn} from "tc-shared/log"
|
|||
import * as i18n from "tc-shared/i18n/localize";
|
||||
import {RepositoryTranslation, TranslationRepository} from "tc-shared/i18n/localize";
|
||||
import {Registry} from "tc-shared/events";
|
||||
import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo";
|
||||
import * as i18nc from "tc-shared/i18n/country";
|
||||
import * as forum from "tc-shared/profiles/identities/teaspeak-forum";
|
||||
import {formatMessage, set_icon_size} from "tc-shared/ui/frames/chat";
|
||||
|
@ -27,6 +26,7 @@ import {initialize_audio_microphone_controller} from "tc-shared/ui/modal/setting
|
|||
import {MicrophoneSettings} from "tc-shared/ui/modal/settings/MicrophoneRenderer";
|
||||
import {getBackend} from "tc-shared/backend";
|
||||
import {MicrophoneSettingsEvents} from "tc-shared/ui/modal/settings/MicrophoneDefinitions";
|
||||
import {promptYesNo} from "tc-shared/ui/modal/yes-no/Controller";
|
||||
|
||||
type ProfileInfoEvent = {
|
||||
id: string,
|
||||
|
@ -411,7 +411,10 @@ function settings_general_language(container: JQuery, modal: Modal) {
|
|||
repo_tag.find(".button-delete").on('click', e => {
|
||||
e.preventDefault();
|
||||
|
||||
spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this repository?"), answer => {
|
||||
promptYesNo({
|
||||
title: tr("Are you sure?"),
|
||||
question: tr("Do you really want to delete this repository?"),
|
||||
}).then(answer => {
|
||||
if (answer) {
|
||||
i18n.delete_repository(repo);
|
||||
update_list();
|
||||
|
@ -1323,7 +1326,10 @@ export namespace modal_settings {
|
|||
button.on('click', event => {
|
||||
if (!current_profile || current_profile === "default") return;
|
||||
|
||||
spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this profile?"), result => {
|
||||
promptYesNo({
|
||||
title: tr("Are you sure?"),
|
||||
question: tr("Do you really want to delete this profile?"),
|
||||
}).then(result => {
|
||||
if (result)
|
||||
event_registry.fire("delete-profile", {profile_id: current_profile});
|
||||
});
|
||||
|
@ -1646,7 +1652,10 @@ export namespace modal_settings {
|
|||
{
|
||||
button_new.on('click', event => {
|
||||
if (is_profile_generated) {
|
||||
spawnYesNo(tr("Are you sure"), tr("Do you really want to generate a new identity and override the old identity?"), result => {
|
||||
promptYesNo({
|
||||
title: tr("Are you sure"),
|
||||
question: tr("Do you really want to generate a new identity and override the old identity?"),
|
||||
}).then(result => {
|
||||
if (result) event_registry.fire("generate-identity-teamspeak", {profile_id: current_profile});
|
||||
});
|
||||
} else {
|
||||
|
@ -1671,7 +1680,10 @@ export namespace modal_settings {
|
|||
{
|
||||
button_import.on('click', event => {
|
||||
if (is_profile_generated) {
|
||||
spawnYesNo(tr("Are you sure"), tr("Do you really want to import a new identity and override the old identity?"), result => {
|
||||
promptYesNo({
|
||||
title: tr("Are you sure"),
|
||||
question: tr("Do you really want to import a new identity and override the old identity?"),
|
||||
}).then(result => {
|
||||
if (result) event_registry.fire("import-identity-teamspeak", {profile_id: current_profile});
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import {BodyCreator, createModal, ModalFunctions} from "../../ui/elements/Modal";
|
||||
import {BodyCreator, createModal, Modal, ModalFunctions} from "../../ui/elements/Modal";
|
||||
|
||||
export function spawnYesNo(header: BodyCreator, body: BodyCreator, callback: (_: boolean) => any, properties?: {
|
||||
text_yes?: string,
|
||||
text_no?: string,
|
||||
|
||||
closeable?: boolean;
|
||||
}) {
|
||||
}) : Modal {
|
||||
properties = properties || {};
|
||||
|
||||
const props = ModalFunctions.warpProperties({});
|
||||
|
|
|
@ -8,7 +8,7 @@ import PermissionType from "tc-shared/permission/PermissionType";
|
|||
import {getOwnAvatarStorage, LocalAvatarInfo} from "tc-shared/file/OwnAvatarStorage";
|
||||
import {LogCategory, logError, logInfo, logWarn} from "tc-shared/log";
|
||||
import {Mutex} from "tc-shared/Mutex";
|
||||
import {tr, trJQuery} from "tc-shared/i18n/localize";
|
||||
import {tr, tra, trJQuery} from "tc-shared/i18n/localize";
|
||||
import {createErrorModal, createInfoModal} from "tc-shared/ui/elements/Modal";
|
||||
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {formatMessage} from "tc-shared/ui/frames/chat";
|
||||
|
@ -262,7 +262,7 @@ class Controller {
|
|||
if (transfer.transferState() !== FileTransferState.FINISHED) {
|
||||
if (transfer.transferState() === FileTransferState.ERRORED) {
|
||||
logWarn(LogCategory.FILE_TRANSFER, tr("Failed to upload clients avatar: %o"), transfer.currentError());
|
||||
createErrorModal(tr("Failed to upload avatar"), tr("Failed to upload avatar:\n{0}", transfer.currentErrorMessage())).open();
|
||||
createErrorModal(tr("Failed to upload avatar"), tra("Failed to upload avatar:\n{0}", transfer.currentErrorMessage())).open();
|
||||
return;
|
||||
} else if (transfer.transferState() === FileTransferState.CANCELED) {
|
||||
createErrorModal(tr("Failed to upload avatar"), tr("Your avatar upload has been canceled.")).open();
|
||||
|
|
|
@ -19,7 +19,6 @@ import {getIconManager} from "tc-shared/file/Icons";
|
|||
import {ClientIcon} from "svg-sprites/client-icons";
|
||||
import {ClientIconRenderer} from "tc-shared/ui/react-elements/Icons";
|
||||
import {spawnContextMenu} from "tc-shared/ui/ContextMenu";
|
||||
import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo";
|
||||
import {formatMessage} from "tc-shared/ui/frames/chat";
|
||||
import {createErrorModal, createInfoModal, createInputModal} from "tc-shared/ui/elements/Modal";
|
||||
import {HostBannerRenderer} from "tc-shared/ui/frames/HostBannerRenderer";
|
||||
|
@ -32,6 +31,8 @@ import ServerInfoImage from "./serverinfo.png";
|
|||
import {IconTooltip} from "tc-shared/ui/react-elements/Tooltip";
|
||||
import {CountryCode} from "tc-shared/ui/react-elements/CountryCode";
|
||||
import {downloadTextAsFile, requestFileAsText} from "tc-shared/file/Utils";
|
||||
import {promptYesNo} from "tc-shared/ui/modal/yes-no/Controller";
|
||||
import {tra} from "tc-shared/i18n/localize";
|
||||
|
||||
const EventContext = React.createContext<Registry<ModalBookmarkEvents>>(undefined);
|
||||
const VariableContext = React.createContext<UiVariableConsumer<ModalBookmarkVariables>>(undefined);
|
||||
|
@ -54,14 +55,16 @@ const BookmarkListEntryRenderer = React.memo((props: { entry: BookmarkListEntry
|
|||
|
||||
const tryDelete = () => {
|
||||
if(props.entry.type === "directory" && props.entry.childCount > 0) {
|
||||
spawnYesNo(tr("Are you sure?"), formatMessage(
|
||||
tr("Do you really want to delete the directory \"{0}\"?{:br:}The directory contains {1} entries."),
|
||||
props.entry.displayName, props.entry.childCount
|
||||
), result => {
|
||||
if(result) {
|
||||
events.fire("action_delete_bookmark", { uniqueId: props.entry.uniqueId });
|
||||
promptYesNo({
|
||||
title: tr("Are you sure?"),
|
||||
question: tra("Do you really want to delete the directory \"{0}\"?\nThe directory contains {1} entries.", props.entry.displayName, props.entry.childCount)
|
||||
}).then(result => {
|
||||
if(!result) {
|
||||
return;
|
||||
}
|
||||
}).open();
|
||||
|
||||
events.fire("action_delete_bookmark", { uniqueId: props.entry.uniqueId });
|
||||
});
|
||||
} else {
|
||||
events.fire("action_delete_bookmark", { uniqueId: props.entry.uniqueId });
|
||||
}
|
||||
|
|
|
@ -5,8 +5,7 @@ import {DefaultTabValues} from "tc-shared/ui/modal/permission/ModalRenderer";
|
|||
import {Group, GroupTarget, GroupType} from "tc-shared/permission/GroupManager";
|
||||
import {createErrorModal, createInfoModal} from "tc-shared/ui/elements/Modal";
|
||||
import {ClientNameInfo, CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {formatMessage} from "tc-shared/ui/frames/chat";
|
||||
import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo";
|
||||
import {formatMessage, formatMessageString} from "tc-shared/ui/frames/chat";
|
||||
import {tra} from "tc-shared/i18n/localize";
|
||||
import {PermissionType} from "tc-shared/permission/PermissionType";
|
||||
import {GroupedPermissions, PermissionValue} from "tc-shared/permission/PermissionManager";
|
||||
|
@ -32,6 +31,7 @@ import {
|
|||
PermissionModalEvents
|
||||
} from "tc-shared/ui/modal/permission/ModalDefinitions";
|
||||
import {EditorGroupedPermissions, PermissionEditorEvents} from "tc-shared/ui/modal/permission/EditorDefinitions";
|
||||
import {promptYesNo} from "tc-shared/ui/modal/yes-no/Controller";
|
||||
|
||||
export function spawnPermissionEditorModal(connection: ConnectionHandler, defaultTab: PermissionEditorTab = "groups-server", defaultTabValues?: DefaultTabValues) {
|
||||
const modalEvents = new Registry<PermissionModalEvents>();
|
||||
|
@ -96,7 +96,10 @@ function initializePermissionModalResultHandlers(events: Registry<PermissionModa
|
|||
if (event.mode === "force")
|
||||
return;
|
||||
|
||||
spawnYesNo(tr("Are you sure?"), formatMessage(tr("Do you really want to delete this group?")), result => {
|
||||
promptYesNo({
|
||||
title: tr("Are you sure?"),
|
||||
question: formatMessageString(tr("Do you really want to delete this group?")),
|
||||
}).then(result => {
|
||||
if (result !== true)
|
||||
return;
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ import PermissionType from "../../../permission/PermissionType";
|
|||
import {LogCategory, logError, logTrace} from "../../../log";
|
||||
import {Entry, MenuEntry, MenuEntryType, spawn_context_menu} from "../../../ui/elements/ContextMenu";
|
||||
import {getKeyBoard, SpecialKey} from "../../../PPTListener";
|
||||
import {spawnYesNo} from "../../../ui/modal/ModalYesNo";
|
||||
import {tr, tra} from "../../../i18n/localize";
|
||||
import {
|
||||
FileTransfer,
|
||||
|
@ -25,6 +24,7 @@ import {
|
|||
ListedFileInfo,
|
||||
PathInfo
|
||||
} from "tc-shared/ui/modal/transfer/FileDefinitions";
|
||||
import {promptYesNo} from "tc-shared/ui/modal/yes-no/Controller";
|
||||
|
||||
function parsePath(path: string, connection: ConnectionHandler): PathInfo {
|
||||
if (path === "/" || !path) {
|
||||
|
@ -508,8 +508,14 @@ export function initializeRemoteFileBrowserController(connection: ConnectionHand
|
|||
}) : event.files;
|
||||
|
||||
if (event.mode === "ask") {
|
||||
spawnYesNo(tr("Are you sure?"), tra("Do you really want to delete {0} {1}?", files.length, files.length === 1 ? tr("files") : tr("files")), result => {
|
||||
if (result)
|
||||
promptYesNo({
|
||||
title: tr("Are you sure?"),
|
||||
question: tra("Do you really want to delete {0} {1}?", files.length, files.length === 1 ? tr("files") : tr("files")),
|
||||
}).then(result => {
|
||||
if(!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
events.fire("action_delete_file", {
|
||||
files: files,
|
||||
mode: "force"
|
||||
|
|
|
@ -1,3 +1,33 @@
|
|||
import {spawnModal} from "tc-shared/ui/react-elements/modal";
|
||||
import {Registry} from "tc-events";
|
||||
import {ModalYesNoEvents, ModalYesNoVariables} from "tc-shared/ui/modal/yes-no/Definitions";
|
||||
import {IpcUiVariableProvider} from "tc-shared/ui/utils/IpcVariable";
|
||||
import {CallOnce, ignorePromise} from "tc-shared/proto";
|
||||
|
||||
class Controller {
|
||||
readonly properties: YesNoParameters;
|
||||
readonly events: Registry<ModalYesNoEvents>;
|
||||
readonly variables: IpcUiVariableProvider<ModalYesNoVariables>;
|
||||
|
||||
constructor(properties: YesNoParameters) {
|
||||
this.properties = properties;
|
||||
this.events = new Registry<ModalYesNoEvents>();
|
||||
this.variables = new IpcUiVariableProvider<ModalYesNoVariables>();
|
||||
|
||||
this.variables.setVariableProvider("title", () => this.properties.title);
|
||||
this.variables.setVariableProvider("question", () => this.properties.question);
|
||||
|
||||
this.variables.setVariableProvider("textYes", () => this.properties.textYes);
|
||||
this.variables.setVariableProvider("textNo", () => this.properties.textNo);
|
||||
}
|
||||
|
||||
@CallOnce
|
||||
destroy() {
|
||||
this.events.destroy();
|
||||
this.variables.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
export interface YesNoParameters {
|
||||
title: string,
|
||||
question: string,
|
||||
|
@ -20,5 +50,23 @@ export async function promptYesNo(properties: YesNoParameters) : Promise<boolean
|
|||
throw "yes-no question isn't a string";
|
||||
}
|
||||
|
||||
return false;
|
||||
const controller = new Controller(properties);
|
||||
const modal = spawnModal("modal-yes-no", [
|
||||
controller.events.generateIpcDescription(),
|
||||
controller.variables.generateConsumerDescription()
|
||||
], {
|
||||
popoutable: false,
|
||||
destroyOnClose: true
|
||||
});
|
||||
|
||||
modal.getEvents().on("destroy", () => controller.destroy());
|
||||
ignorePromise(modal.show());
|
||||
|
||||
return await new Promise<boolean | undefined>(resolve => {
|
||||
modal.getEvents().on("destroy", () => resolve());
|
||||
controller.events.on("action_submit", event => {
|
||||
resolve(event.status);
|
||||
modal.destroy();
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
export interface ModalYesNoVariables {
|
||||
readonly title: string,
|
||||
readonly question: string,
|
||||
|
||||
readonly textYes: string | undefined,
|
||||
readonly textNo: string | undefined,
|
||||
}
|
||||
|
||||
export interface ModalYesNoEvents {
|
||||
action_submit: { status: boolean }
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
.container {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.question {
|
||||
min-height: 1.6em;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
|
||||
margin-top: 2em;
|
||||
|
||||
button {
|
||||
min-width: 6em;
|
||||
|
||||
&:not(:last-of-type) {
|
||||
margin-right: 1em;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
import {AbstractModal} from "tc-shared/ui/react-elements/modal/Definitions";
|
||||
import {IpcRegistryDescription, Registry} from "tc-events";
|
||||
import {ModalYesNoEvents, ModalYesNoVariables} from "tc-shared/ui/modal/yes-no/Definitions";
|
||||
import {UiVariableConsumer} from "tc-shared/ui/utils/Variable";
|
||||
import {createIpcUiVariableConsumer} from "tc-shared/ui/utils/IpcVariable";
|
||||
import React from "react";
|
||||
import {Translatable} from "tc-shared/ui/react-elements/i18n";
|
||||
import {Button} from "tc-shared/ui/react-elements/Button";
|
||||
const cssStyle = require("./Renderer.scss");
|
||||
|
||||
const QuestionRenderer = React.memo((props: { variables: UiVariableConsumer<ModalYesNoVariables> }) => {
|
||||
const question = props.variables.useReadOnly("question", undefined, undefined);
|
||||
return (
|
||||
<div className={cssStyle.question}>
|
||||
{question}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
const TitleRenderer = React.memo((props: { variables: UiVariableConsumer<ModalYesNoVariables> }) => {
|
||||
const title = props.variables.useReadOnly("title", undefined, undefined);
|
||||
if(typeof title !== "undefined") {
|
||||
return <React.Fragment key={"loaded"}>{title}</React.Fragment>;
|
||||
} else {
|
||||
return <Translatable key={"loading"}>loading</Translatable>;
|
||||
}
|
||||
});
|
||||
|
||||
const TextYes = React.memo((props: { variables: UiVariableConsumer<ModalYesNoVariables> }) => {
|
||||
const text = props.variables.useReadOnly("textYes", undefined, undefined);
|
||||
if(typeof text !== "undefined") {
|
||||
return <React.Fragment key={"custom"}>{text}</React.Fragment>;
|
||||
} else {
|
||||
return <Translatable key={"default"}>Yes</Translatable>;
|
||||
}
|
||||
});
|
||||
|
||||
const TextNo = React.memo((props: { variables: UiVariableConsumer<ModalYesNoVariables> }) => {
|
||||
const text = props.variables.useReadOnly("textNo", undefined, undefined);
|
||||
if(typeof text !== "undefined") {
|
||||
return <React.Fragment key={"custom"}>{text}</React.Fragment>;
|
||||
} else {
|
||||
return <Translatable key={"default"}>No</Translatable>;
|
||||
}
|
||||
});
|
||||
|
||||
class Modal extends AbstractModal {
|
||||
private readonly events: Registry<ModalYesNoEvents>;
|
||||
private readonly variables: UiVariableConsumer<ModalYesNoVariables>;
|
||||
|
||||
constructor(events: IpcRegistryDescription<ModalYesNoEvents>, variables: IpcRegistryDescription<ModalYesNoVariables>) {
|
||||
super();
|
||||
|
||||
this.events = Registry.fromIpcDescription(events);
|
||||
this.variables = createIpcUiVariableConsumer(variables);
|
||||
}
|
||||
|
||||
protected onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
this.events.destroy();
|
||||
this.variables.destroy();
|
||||
}
|
||||
|
||||
color(): "none" | "blue" | "red" {
|
||||
return "red";
|
||||
}
|
||||
|
||||
renderBody(): React.ReactElement {
|
||||
return (
|
||||
<div className={cssStyle.container}>
|
||||
<QuestionRenderer variables={this.variables} />
|
||||
<div className={cssStyle.buttons}>
|
||||
<Button color={"red"} onClick={() => this.events.fire("action_submit", { status: false })}>
|
||||
<TextNo variables={this.variables} />
|
||||
</Button>
|
||||
<Button color={"green"} onClick={() => this.events.fire("action_submit", { status: true })}>
|
||||
<TextYes variables={this.variables} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderTitle(): string | React.ReactElement {
|
||||
return (
|
||||
<TitleRenderer variables={this.variables} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Modal;
|
|
@ -24,6 +24,7 @@ import {ModalInputProcessorEvents, ModalInputProcessorVariables} from "tc-shared
|
|||
import {ModalServerInfoEvents, ModalServerInfoVariables} from "tc-shared/ui/modal/server-info/Definitions";
|
||||
import {ModalAboutVariables} from "tc-shared/ui/modal/about/Definitions";
|
||||
import {ModalServerBandwidthEvents} from "tc-shared/ui/modal/server-bandwidth/Definitions";
|
||||
import {ModalYesNoEvents, ModalYesNoVariables} from "tc-shared/ui/modal/yes-no/Definitions";
|
||||
|
||||
export type ModalType = "error" | "warning" | "info" | "none";
|
||||
export type ModalRenderType = "page" | "dialog";
|
||||
|
@ -138,7 +139,7 @@ export abstract class AbstractModal {
|
|||
|
||||
/* only valid for the "inline" modals */
|
||||
type() : ModalType { return "none"; }
|
||||
color() : "none" | "blue" { return "none"; }
|
||||
color() : "none" | "blue" | "red" { return "none"; }
|
||||
verticalAlignment() : "top" | "center" | "bottom" { return "center"; }
|
||||
|
||||
/** @deprecated */
|
||||
|
@ -165,6 +166,10 @@ export function constructAbstractModalClass<T extends keyof ModalConstructorArgu
|
|||
|
||||
export interface ModalConstructorArguments {
|
||||
"__internal__modal__": any[],
|
||||
"modal-yes-no": [
|
||||
/* events */ IpcRegistryDescription<ModalYesNoEvents>,
|
||||
/* variables */ IpcVariableDescriptor<ModalYesNoVariables>,
|
||||
],
|
||||
|
||||
"video-viewer": [
|
||||
/* events */ IpcRegistryDescription<VideoViewerEvents>,
|
||||
|
|
|
@ -19,6 +19,12 @@ function registerModal<T extends keyof ModalConstructorArguments>(modal: Registe
|
|||
registeredModals[modal.modalId] = modal as any;
|
||||
}
|
||||
|
||||
registerModal({
|
||||
modalId: "modal-yes-no",
|
||||
classLoader: async () => await import("tc-shared/ui/modal/yes-no/Renderer"),
|
||||
popoutSupported: true
|
||||
})
|
||||
|
||||
registerModal({
|
||||
modalId: "video-viewer",
|
||||
classLoader: async () => await import("tc-shared/ui/modal/video-viewer/Renderer"),
|
||||
|
|
|
@ -13,5 +13,3 @@ export class UnreadMarkerRenderer extends React.Component<{ entry: RDPEntry }, {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class RendererTreeEntry<Props, State> extends ReactComponentBase<Props, State> { }
|
|
@ -8,10 +8,10 @@ import {
|
|||
import {assertMainApplication} from "tc-shared/ui/utils";
|
||||
import {Registry} from "tc-events";
|
||||
import {getIpcInstance} from "tc-shared/ipc/BrowserIPC";
|
||||
import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo";
|
||||
import {tr, tra} from "tc-shared/i18n/localize";
|
||||
import {guid} from "tc-shared/crypto/uid";
|
||||
import _ from "lodash";
|
||||
import {promptYesNo} from "tc-shared/ui/modal/yes-no/Controller";
|
||||
|
||||
assertMainApplication();
|
||||
|
||||
|
@ -64,17 +64,16 @@ export class WebWindowManager implements WindowManager {
|
|||
let windowInstance = this.tryCreateWindow(options, windowUniqueId);
|
||||
if(!windowInstance) {
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
spawnYesNo(tr("Would you like to open the popup?"), tra("Would you like to open window {}?", options.windowName), callback => {
|
||||
if(!callback) {
|
||||
reject();
|
||||
return;
|
||||
const result = await promptYesNo({
|
||||
title: tr("Would you like to open the popup?"),
|
||||
question: tra("Would you like to open window {}?", options.windowName)
|
||||
});
|
||||
|
||||
if(!result) {
|
||||
return { status: "error-user-rejected" };
|
||||
}
|
||||
|
||||
windowInstance = this.tryCreateWindow(options, windowUniqueId);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
} catch (_) {
|
||||
return { status: "error-user-rejected" };
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue