--- /dev/null
+import React from 'react';
+import PropTypes from 'prop-types';
+import './ConfirmDialog.scss';
+
+import { openReusableDialog } from '../../../client/action/navigation';
+
+import Text from '../../atoms/text/Text';
+import Button from '../../atoms/button/Button';
+
+function ConfirmDialog({
+ desc, actionTitle, actionType, onComplete,
+}) {
+ return (
+ <div className="confirm-dialog">
+ <Text>{desc}</Text>
+ <div className="confirm-dialog__btn">
+ <Button variant={actionType} onClick={() => onComplete(true)}>{actionTitle}</Button>
+ <Button onClick={() => onComplete(false)}>Cancel</Button>
+ </div>
+ </div>
+ );
+}
+ConfirmDialog.propTypes = {
+ desc: PropTypes.string.isRequired,
+ actionTitle: PropTypes.string.isRequired,
+ actionType: PropTypes.oneOf(['primary', 'positive', 'danger', 'caution']).isRequired,
+ onComplete: PropTypes.func.isRequired,
+};
+
+/**
+ * @param {string} title title of confirm dialog
+ * @param {string} desc description of confirm dialog
+ * @param {string} actionTitle title of main action to take
+ * @param {'primary' | 'positive' | 'danger' | 'caution'} actionType type of action. default=primary
+ * @return {Promise<boolean>} does it get's confirmed or not
+ */
+// eslint-disable-next-line import/prefer-default-export
+export const confirmDialog = (title, desc, actionTitle, actionType = 'primary') => new Promise((resolve) => {
+ let isCompleted = false;
+ openReusableDialog(
+ <Text variant="s1" weight="medium">{title}</Text>,
+ (requestClose) => (
+ <ConfirmDialog
+ desc={desc}
+ actionTitle={actionTitle}
+ actionType={actionType}
+ onComplete={(isConfirmed) => {
+ isCompleted = true;
+ resolve(isConfirmed);
+ requestClose();
+ }}
+ />
+ ),
+ () => {
+ if (!isCompleted) resolve(false);
+ },
+ );
+});
--- /dev/null
+.confirm-dialog {
+ padding: var(--sp-normal);
+
+ & > .text {
+ padding-bottom: var(--sp-normal);
+ }
+ &__btn {
+ display: flex;
+ gap: var(--sp-normal);
+ }
+}
\ No newline at end of file
import CmdIC from '../../../../public/res/ic/outlined/cmd.svg';
import BinIC from '../../../../public/res/ic/outlined/bin.svg';
+import { confirmDialog } from '../confirm-dialog/ConfirmDialog';
+
function PlaceholderMessage() {
return (
<div className="ph-msg">
<MenuItem
variant="danger"
iconSrc={BinIC}
- onClick={() => {
- if (window.confirm('Are you sure that you want to delete this event?')) {
- redactEvent(roomId, mEvent.getId());
- }
+ onClick={async () => {
+ const isConfirmed = await confirmDialog(
+ 'Delete message',
+ 'Are you sure that you want to delete this message?',
+ 'Delete',
+ 'danger',
+ );
+ if (!isConfirmed) return;
+ redactEvent(roomId, mEvent.getId());
}}
>
Delete
import Toggle from '../../atoms/button/Toggle';
import SettingTile from '../setting-tile/SettingTile';
+import { confirmDialog } from '../confirm-dialog/ConfirmDialog';
+
function RoomEncryption({ roomId }) {
const mx = initMatrix.matrixClient;
const room = mx.getRoom(roomId);
const [isEncrypted, setIsEncrypted] = useState(encryptionEvents.length > 0);
const canEnableEncryption = room.currentState.maySendStateEvent('m.room.encryption', mx.getUserId());
- const handleEncryptionEnable = () => {
+ const handleEncryptionEnable = async () => {
const joinRule = room.getJoinRule();
const confirmMsg1 = 'It is not recommended to add encryption in public room. Anyone can find and join public rooms, so anyone can read messages in them.';
const confirmMsg2 = 'Once enabled, encryption for a room cannot be disabled. Messages sent in an encrypted room cannot be seen by the server, only by the participants of the room. Enabling encryption may prevent many bots and bridges from working correctly';
- if (joinRule === 'public' ? confirm(confirmMsg1) : true) {
- if (confirm(confirmMsg2)) {
- setIsEncrypted(true);
- mx.sendStateEvent(roomId, 'm.room.encryption', {
- algorithm: 'm.megolm.v1.aes-sha2',
- });
- }
+
+ const isConfirmed1 = (joinRule === 'public')
+ ? await confirmDialog('Enable encryption', confirmMsg1, 'Continue', 'caution')
+ : true;
+ if (!isConfirmed1) return;
+ if (await confirmDialog('Enable encryption', confirmMsg2, 'Enable', 'caution')) {
+ setIsEncrypted(true);
+ mx.sendStateEvent(roomId, 'm.room.encryption', {
+ algorithm: 'm.megolm.v1.aes-sha2',
+ });
}
};
import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix';
-import { openInviteUser, openNavigation } from '../../../client/action/navigation';
+import { openInviteUser } from '../../../client/action/navigation';
import * as roomActions from '../../../client/action/room';
import { markAsRead } from '../../../client/action/notifications';
import AddUserIC from '../../../../public/res/ic/outlined/add-user.svg';
import LeaveArrowIC from '../../../../public/res/ic/outlined/leave-arrow.svg';
+import { confirmDialog } from '../confirm-dialog/ConfirmDialog';
+
function RoomOptions({ roomId, afterOptionSelect }) {
const mx = initMatrix.matrixClient;
const room = mx.getRoom(roomId);
openInviteUser(roomId);
afterOptionSelect();
};
- const handleLeaveClick = () => {
- if (confirm('Are you sure that you want to leave this room?')) {
- roomActions.leave(roomId);
- afterOptionSelect();
- openNavigation();
- }
+ const handleLeaveClick = async () => {
+ afterOptionSelect();
+ const isConfirmed = await confirmDialog(
+ 'Leave room',
+ `Are you sure that you want to leave "${room.name}" room?`,
+ 'Leave',
+ 'danger',
+ );
+ if (!isConfirmed) return;
+ roomActions.leave(roomId);
};
return (
import { useStore } from '../../hooks/useStore';
import { useForceUpdate } from '../../hooks/useForceUpdate';
+import { confirmDialog } from '../confirm-dialog/ConfirmDialog';
function RoomProfile({ roomId }) {
const isMountStore = useStore();
const handleAvatarUpload = async (url) => {
if (url === null) {
- if (confirm('Are you sure that you want to remove room avatar?')) {
+ const isConfirmed = await confirmDialog(
+ 'Remove avatar',
+ 'Are you sure that you want to remove room avatar?',
+ 'Remove',
+ 'caution',
+ );
+ if (isConfirmed) {
await mx.sendStateEvent(roomId, 'm.room.avatar', { url }, '');
}
} else await mx.sendStateEvent(roomId, 'm.room.avatar', { url }, '');
import PinIC from '../../../../public/res/ic/outlined/pin.svg';
import PinFilledIC from '../../../../public/res/ic/filled/pin.svg';
+import { confirmDialog } from '../confirm-dialog/ConfirmDialog';
+
function SpaceOptions({ roomId, afterOptionSelect }) {
const mx = initMatrix.matrixClient;
const room = mx.getRoom(roomId);
afterOptionSelect();
};
- const handleLeaveClick = () => {
- if (confirm('Are you sure that you want to leave this space?')) {
- leave(roomId);
- afterOptionSelect();
- }
+ const handleLeaveClick = async () => {
+ afterOptionSelect();
+ const isConfirmed = await confirmDialog(
+ 'Leave space',
+ `Are you sure that you want to leave "${room.name}" space?`,
+ 'Leave',
+ 'danger',
+ );
+ if (!isConfirmed) return;
+ leave(roomId);
};
return (
import Input from '../../atoms/input/Input';
import PencilIC from '../../../../public/res/ic/outlined/pencil.svg';
+import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
import './ProfileEditor.scss';
};
}, [userId]);
- const handleAvatarUpload = (url) => {
+ const handleAvatarUpload = async (url) => {
if (url === null) {
- if (confirm('Are you sure that you want to remove avatar?')) {
+ const isConfirmed = await confirmDialog(
+ 'Remove avatar',
+ 'Are you sure that you want to remove avatar?',
+ 'Remove',
+ 'caution',
+ );
+ if (isConfirmed) {
mx.setAvatarUrl('');
setAvatarSrc(null);
}
import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
import { useForceUpdate } from '../../hooks/useForceUpdate';
+import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
function ModerationTools({
roomId, userId,
&& (powerLevel < myPowerLevel || userId === mx.getUserId())
);
- const handleChangePowerLevel = (newPowerLevel) => {
+ const handleChangePowerLevel = async (newPowerLevel) => {
if (newPowerLevel === powerLevel) return;
const SHARED_POWER_MSG = 'You will not be able to undo this change as you are promoting the user to have the same power level as yourself. Are you sure?';
const DEMOTING_MYSELF_MSG = 'You will not be able to undo this change as you are demoting yourself. Are you sure?';
const isSharedPower = newPowerLevel === myPowerLevel;
const isDemotingMyself = userId === mx.getUserId();
if (isSharedPower || isDemotingMyself) {
- if (confirm(isSharedPower ? SHARED_POWER_MSG : DEMOTING_MYSELF_MSG)) {
- roomActions.setPowerLevel(roomId, userId, newPowerLevel);
- }
+ const isConfirmed = await confirmDialog(
+ 'Change power level',
+ isSharedPower ? SHARED_POWER_MSG : DEMOTING_MYSELF_MSG,
+ 'Change',
+ 'caution',
+ );
+ if (!isConfirmed) return;
+ roomActions.setPowerLevel(roomId, userId, newPowerLevel);
} else {
roomActions.setPowerLevel(roomId, userId, newPowerLevel);
}
import ChevronTopIC from '../../../../public/res/ic/outlined/chevron-top.svg';
import { useForceUpdate } from '../../hooks/useForceUpdate';
+import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
const tabText = {
GENERAL: 'General',
</MenuItem>
<MenuItem
variant="danger"
- onClick={() => {
- if (confirm('Are you sure that you want to leave this room?')) {
- roomActions.leave(roomId);
- }
+ onClick={async () => {
+ const isConfirmed = await confirmDialog(
+ 'Leave room',
+ `Are you sure that you want to leave "${room.name}" room?`,
+ 'Leave',
+ 'danger',
+ );
+ if (!isConfirmed) return;
+ roomActions.leave(roomId);
}}
iconSrc={LeaveArrowIC}
>
import initMatrix from '../../../client/initMatrix';
import { isCrossVerified } from '../../../util/matrixUtil';
+import { openReusableDialog } from '../../../client/action/navigation';
import Text from '../../atoms/text/Text';
import Button from '../../atoms/button/Button';
import IconButton from '../../atoms/button/IconButton';
+import Input from '../../atoms/input/Input';
import { MenuHeader } from '../../atoms/context-menu/ContextMenu';
import InfoCard from '../../atoms/card/InfoCard';
import Spinner from '../../atoms/spinner/Spinner';
import InfoIC from '../../../../public/res/ic/outlined/info.svg';
import { authRequest } from './AuthRequest';
+import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
import { useStore } from '../../hooks/useStore';
import { useDeviceList } from '../../hooks/useDeviceList';
import { useCrossSigningStatus } from '../../hooks/useCrossSigningStatus';
+const promptDeviceName = async (deviceName) => new Promise((resolve) => {
+ let isCompleted = false;
+
+ const renderContent = (onComplete) => {
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ const name = e.target.session.value;
+ if (typeof name !== 'string') onComplete(null);
+ onComplete(name);
+ };
+ return (
+ <form className="device-manage__rename" onSubmit={handleSubmit}>
+ <Input value={deviceName} label="Session name" name="session" />
+ <div className="device-manage__rename-btn">
+ <Button variant="primary" type="submit">Save</Button>
+ <Button onClick={() => onComplete(null)}>Cancel</Button>
+ </div>
+ </form>
+ );
+ };
+
+ openReusableDialog(
+ <Text variant="s1" weight="medium">Edit session name</Text>,
+ (requestClose) => renderContent((name) => {
+ isCompleted = true;
+ resolve(name);
+ requestClose();
+ }),
+ () => {
+ if (!isCompleted) resolve(null);
+ },
+ );
+});
+
function DeviceManage() {
const TRUNCATED_COUNT = 4;
const mx = initMatrix.matrixClient;
}
const handleRename = async (device) => {
- const newName = window.prompt('Edit session name', device.display_name);
+ const newName = await promptDeviceName(device.display_name);
if (newName === null || newName.trim() === '') return;
if (newName.trim() === device.display_name) return;
addToProcessing(device);
};
const handleRemove = async (device) => {
- if (window.confirm(`You are about to logout "${device.display_name}" session.`)) {
- addToProcessing(device);
- await authRequest(`Logout "${device.display_name}"`, async (auth) => {
- await mx.deleteDevice(device.device_id, auth);
- });
+ const isConfirmed = await confirmDialog(
+ `Logout ${device.display_name}`,
+ `You are about to logout "${device.display_name}" session.`,
+ 'Logout',
+ 'danger',
+ );
+ if (!isConfirmed) return;
+ addToProcessing(device);
+ await authRequest(`Logout "${device.display_name}"`, async (auth) => {
+ await mx.deleteDevice(device.device_id, auth);
+ });
- if (!mountStore.getItem()) return;
- removeFromProcessing(device);
- }
+ if (!mountStore.getItem()) return;
+ removeFromProcessing(device);
};
const renderDevice = (device, isVerified) => {
& .setting-tile:last-of-type {
border-bottom: none;
}
+
+ &__rename {
+ padding: var(--sp-normal);
+ & > *:not(:last-child) {
+ margin-bottom: var(--sp-normal);
+ }
+ &-btn {
+ display: flex;
+ gap: var(--sp-normal);
+ }
+ }
}
\ No newline at end of file
import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
import CinnySVG from '../../../../public/res/svg/cinny.svg';
+import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
function AppearanceSection() {
const [, updateState] = useState({});
const [isOpen, requestClose] = useWindowToggle(setSelectedTab);
const handleTabChange = (tabItem) => setSelectedTab(tabItem);
- const handleLogout = () => {
- if (confirm('Confirm logout')) logout();
+ const handleLogout = async () => {
+ if (await confirmDialog('Logout', 'Are you sure that you want to logout your session?', 'Logout', 'danger')) {
+ logout();
+ }
};
return (
import CategoryIC from '../../../../public/res/ic/outlined/category.svg';
import CategoryFilledIC from '../../../../public/res/ic/filled/category.svg';
+import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
import { useForceUpdate } from '../../hooks/useForceUpdate';
const tabText = {
function GeneralSettings({ roomId }) {
const isPinned = initMatrix.accountData.spaceShortcut.has(roomId);
const isCategorized = initMatrix.accountData.categorizedSpaces.has(roomId);
+ const roomName = initMatrix.matrixClient.getRoom(roomId)?.name;
const [, forceUpdate] = useForceUpdate();
return (
</MenuItem>
<MenuItem
variant="danger"
- onClick={() => {
- if (confirm('Are you sure that you want to leave this space?')) {
- leave(roomId);
- }
+ onClick={async () => {
+ const isConfirmed = await confirmDialog(
+ 'Leave space',
+ `Are you sure that you want to leave "${roomName}" space?`,
+ 'Leave',
+ 'danger',
+ );
+ if (isConfirmed) leave(roomId);
}}
iconSrc={LeaveArrowIC}
>