mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-02-02 14:16:39 +01:00
Replace (most) alert/confirms with alternative UI
Everything might break lol
This commit is contained in:
parent
10fa537a56
commit
ff41cd3563
11 changed files with 423 additions and 124 deletions
75
src/app.css
75
src/app.css
|
@ -1401,7 +1401,7 @@ body > .szh-menu-container {
|
|||
animation: appear-smooth 0.15s ease-in-out;
|
||||
width: 16em;
|
||||
max-width: 90vw;
|
||||
overflow: hidden;
|
||||
/* overflow: hidden; */
|
||||
}
|
||||
.szh-menu[aria-label='Submenu'] {
|
||||
background-color: var(--bg-blur-color);
|
||||
|
@ -1418,6 +1418,7 @@ body > .szh-menu-container {
|
|||
text-shadow: 0 1px 0 var(--bg-color);
|
||||
line-height: 1.2;
|
||||
/* border-bottom: 1px solid var(--outline-color); */
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
.szh-menu__header.plain {
|
||||
margin-bottom: 0;
|
||||
|
@ -1426,6 +1427,28 @@ body > .szh-menu-container {
|
|||
.szh-menu__header * {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.szh-menu.menu-emphasized {
|
||||
border-color: var(--outline-hover-color);
|
||||
box-shadow: 0 3px 16px -3px var(--drop-shadow-color),
|
||||
0 3px 32px var(--drop-shadow-color), 0 3px 48px var(--drop-shadow-color);
|
||||
background-color: var(--bg-color);
|
||||
animation-duration: 0.3s;
|
||||
animation-timing-function: ease-in-out;
|
||||
width: auto;
|
||||
}
|
||||
.szh-menu .footer {
|
||||
margin: 8px 0 -8px;
|
||||
padding: 8px 16px;
|
||||
color: var(--text-insignificant-color);
|
||||
font-size: 90%;
|
||||
background-color: var(--bg-faded-color);
|
||||
text-shadow: 0 1px 0 var(--bg-color);
|
||||
line-height: 1.2;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
border-radius: 0 0 8px 8px;
|
||||
}
|
||||
.szh-menu .szh-menu__item {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
@ -1498,21 +1521,26 @@ body > .szh-menu-container {
|
|||
font-size: inherit;
|
||||
}
|
||||
.szh-menu .menu-horizontal {
|
||||
display: flex;
|
||||
display: grid;
|
||||
/* two columns only */
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
.szh-menu .menu-horizontal .szh-menu__item {
|
||||
flex: 1;
|
||||
}
|
||||
.szh-menu .menu-horizontal .szh-menu__item:not(:only-child):first-child {
|
||||
.szh-menu .menu-horizontal > .szh-menu__item:not(:only-child):first-child,
|
||||
.szh-menu .menu-horizontal > *:not(:only-child):first-child .szh-menu__item {
|
||||
padding-right: 4px !important;
|
||||
}
|
||||
.szh-menu
|
||||
.menu-horizontal
|
||||
.szh-menu__item:not(:only-child):not(:first-child):not(:last-child) {
|
||||
> .szh-menu__item:not(:only-child):not(:first-child):not(:last-child),
|
||||
.szh-menu
|
||||
.menu-horizontal
|
||||
> *:not(:only-child):not(:first-child):not(:last-child)
|
||||
.szh-menu__item {
|
||||
padding-left: 8px !important;
|
||||
padding-right: 4px !important;
|
||||
}
|
||||
.szh-menu .menu-horizontal .szh-menu__item:not(:only-child):last-child {
|
||||
.szh-menu .menu-horizontal > .szh-menu__item:not(:only-child):last-child,
|
||||
.szh-menu .menu-horizontal > *:not(:only-child):last-child .szh-menu__item {
|
||||
padding-left: 8px !important;
|
||||
}
|
||||
.szh-menu .szh-menu__item .menu-shortcut {
|
||||
|
@ -1533,6 +1561,19 @@ body > .szh-menu-container {
|
|||
color: var(--red-color);
|
||||
opacity: 1;
|
||||
}
|
||||
.szh-menu
|
||||
.szh-menu__item:not(.szh-menu__item--disabled):not(
|
||||
.szh-menu__item--hover
|
||||
).danger {
|
||||
color: var(--red-color);
|
||||
}
|
||||
.szh-menu
|
||||
.szh-menu__item:not(.szh-menu__item--disabled):not(
|
||||
.szh-menu__item--hover
|
||||
).danger
|
||||
.icon {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.szh-menu .menu-wrap {
|
||||
display: flex;
|
||||
|
@ -1658,6 +1699,24 @@ meter.donut[hidden] {
|
|||
margin-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
/* TOAST - ALERT */
|
||||
|
||||
:root .toastify.alert {
|
||||
z-index: 1001;
|
||||
box-shadow: 0 8px 32px var(--text-insignificant-color);
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
padding: 16px 32px;
|
||||
font-size: max(calc(16px * 1.1), var(--text-size));
|
||||
text-align: center;
|
||||
line-height: 1.25;
|
||||
}
|
||||
:root .toastify.alert:is(:hover, :active) {
|
||||
background-color: var(--bg-faded-color);
|
||||
}
|
||||
|
||||
/* AVATARS STACK */
|
||||
|
||||
.avatars-stack {
|
||||
|
|
|
@ -21,6 +21,7 @@ import Icon from './icon';
|
|||
import Link from './link';
|
||||
import ListAddEdit from './list-add-edit';
|
||||
import Loader from './loader';
|
||||
import MenuConfirm from './menu-confirm';
|
||||
import Modal from './modal';
|
||||
import TranslationBlock from './translation-block';
|
||||
|
||||
|
@ -734,11 +735,20 @@ function RelatedActions({ info, instance, authenticated }) {
|
|||
</div>
|
||||
</SubMenu>
|
||||
)}
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
if (!blocking && !confirm(`Block @${username}?`)) {
|
||||
return;
|
||||
<MenuConfirm
|
||||
subMenu
|
||||
confirm={!blocking}
|
||||
confirmLabel={
|
||||
<>
|
||||
<Icon icon="block" />
|
||||
<span>Block @{username}?</span>
|
||||
</>
|
||||
}
|
||||
menuItemClassName="danger"
|
||||
onClick={() => {
|
||||
// if (!blocking && !confirm(`Block @${username}?`)) {
|
||||
// return;
|
||||
// }
|
||||
setRelationshipUIState('loading');
|
||||
(async () => {
|
||||
try {
|
||||
|
@ -784,7 +794,7 @@ function RelatedActions({ info, instance, authenticated }) {
|
|||
<span>Block @{username}…</span>
|
||||
</>
|
||||
)}
|
||||
</MenuItem>
|
||||
</MenuConfirm>
|
||||
{/* <MenuItem>
|
||||
<Icon icon="flag" />
|
||||
<span>Report @{username}…</span>
|
||||
|
@ -796,10 +806,17 @@ function RelatedActions({ info, instance, authenticated }) {
|
|||
<Loader abrupt />
|
||||
)}
|
||||
{!!relationship && (
|
||||
<button
|
||||
type="button"
|
||||
class={`${following || requested ? 'light swap' : ''}`}
|
||||
data-swap-state={following || requested ? 'danger' : ''}
|
||||
<MenuConfirm
|
||||
confirm={following || requested}
|
||||
confirmLabel={
|
||||
<span>
|
||||
{requested
|
||||
? 'Withdraw follow request?'
|
||||
: `Unfollow @${info.acct || info.username}?`}
|
||||
</span>
|
||||
}
|
||||
menuItemClassName="danger"
|
||||
align="end"
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
setRelationshipUIState('loading');
|
||||
|
@ -808,18 +825,17 @@ function RelatedActions({ info, instance, authenticated }) {
|
|||
let newRelationship;
|
||||
|
||||
if (following || requested) {
|
||||
const yes = confirm(
|
||||
requested
|
||||
? 'Withdraw follow request?'
|
||||
: `Unfollow @${info.acct || info.username}?`,
|
||||
);
|
||||
// const yes = confirm(
|
||||
// requested
|
||||
// ? 'Withdraw follow request?'
|
||||
// : `Unfollow @${info.acct || info.username}?`,
|
||||
// );
|
||||
|
||||
if (yes) {
|
||||
newRelationship =
|
||||
await currentMasto.v1.accounts.unfollow(
|
||||
// if (yes) {
|
||||
newRelationship = await currentMasto.v1.accounts.unfollow(
|
||||
accountID.current,
|
||||
);
|
||||
}
|
||||
// }
|
||||
} else {
|
||||
newRelationship = await currentMasto.v1.accounts.follow(
|
||||
accountID.current,
|
||||
|
@ -834,6 +850,12 @@ function RelatedActions({ info, instance, authenticated }) {
|
|||
}
|
||||
})();
|
||||
}}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class={`${following || requested ? 'light swap' : ''}`}
|
||||
data-swap-state={following || requested ? 'danger' : ''}
|
||||
disabled={loading}
|
||||
>
|
||||
{following ? (
|
||||
<>
|
||||
|
@ -853,6 +875,7 @@ function RelatedActions({ info, instance, authenticated }) {
|
|||
'Follow'
|
||||
)}
|
||||
</button>
|
||||
</MenuConfirm>
|
||||
)}
|
||||
</span>
|
||||
</p>
|
||||
|
|
|
@ -10,6 +10,7 @@ import { getCurrentAccountNS } from '../utils/store-utils';
|
|||
|
||||
import Icon from './icon';
|
||||
import Loader from './loader';
|
||||
import MenuConfirm from './menu-confirm';
|
||||
|
||||
function Drafts({ onClose }) {
|
||||
const { masto } = api();
|
||||
|
@ -89,26 +90,33 @@ function Drafts({ onClose }) {
|
|||
{niceDateTime(updatedAtDate)}
|
||||
</time>
|
||||
</b>
|
||||
<button
|
||||
type="button"
|
||||
class="small light"
|
||||
<MenuConfirm
|
||||
confirmLabel={<span>Delete this draft?</span>}
|
||||
menuItemClassName="danger"
|
||||
align="end"
|
||||
disabled={uiState === 'loading'}
|
||||
onClick={() => {
|
||||
(async () => {
|
||||
try {
|
||||
const yes = confirm('Delete this draft?');
|
||||
if (yes) {
|
||||
// const yes = confirm('Delete this draft?');
|
||||
// if (yes) {
|
||||
await db.drafts.del(key);
|
||||
reload();
|
||||
}
|
||||
// }
|
||||
} catch (e) {
|
||||
alert('Error deleting draft! Please try again.');
|
||||
}
|
||||
})();
|
||||
}}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="small light"
|
||||
disabled={uiState === 'loading'}
|
||||
>
|
||||
Delete…
|
||||
</button>
|
||||
</MenuConfirm>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
|
@ -145,15 +153,16 @@ function Drafts({ onClose }) {
|
|||
);
|
||||
})}
|
||||
</ul>
|
||||
{drafts.length > 1 && (
|
||||
<p>
|
||||
<button
|
||||
type="button"
|
||||
class="light danger"
|
||||
<MenuConfirm
|
||||
confirmLabel={<span>Delete all drafts?</span>}
|
||||
menuItemClassName="danger"
|
||||
disabled={uiState === 'loading'}
|
||||
onClick={() => {
|
||||
(async () => {
|
||||
const yes = confirm('Delete all drafts?');
|
||||
if (yes) {
|
||||
// const yes = confirm('Delete all drafts?');
|
||||
// if (yes) {
|
||||
setUIState('loading');
|
||||
try {
|
||||
await db.drafts.delMany(
|
||||
|
@ -166,13 +175,20 @@ function Drafts({ onClose }) {
|
|||
alert('Error deleting drafts! Please try again.');
|
||||
setUIState('error');
|
||||
}
|
||||
}
|
||||
// }
|
||||
})();
|
||||
}}
|
||||
>
|
||||
Delete all drafts…
|
||||
<button
|
||||
type="button"
|
||||
class="light danger"
|
||||
disabled={uiState === 'loading'}
|
||||
>
|
||||
Delete all…
|
||||
</button>
|
||||
</MenuConfirm>
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<p>No drafts found.</p>
|
||||
|
|
|
@ -87,6 +87,7 @@ const ICONS = {
|
|||
layout4: () => import('@iconify-icons/mingcute/layout-4-line'),
|
||||
layout5: () => import('@iconify-icons/mingcute/layout-5-line'),
|
||||
announce: () => import('@iconify-icons/mingcute/announcement-line'),
|
||||
alert: () => import('@iconify-icons/mingcute/alert-line'),
|
||||
};
|
||||
|
||||
function Icon({
|
||||
|
|
|
@ -3,6 +3,7 @@ import { useEffect, useRef, useState } from 'preact/hooks';
|
|||
import { api } from '../utils/api';
|
||||
|
||||
import Icon from './icon';
|
||||
import MenuConfirm from './menu-confirm';
|
||||
|
||||
function ListAddEdit({ list, onClose }) {
|
||||
const { masto } = api();
|
||||
|
@ -103,13 +104,14 @@ function ListAddEdit({ list, onClose }) {
|
|||
{editMode ? 'Save' : 'Create'}
|
||||
</button>
|
||||
{editMode && (
|
||||
<button
|
||||
type="button"
|
||||
class="light danger"
|
||||
<MenuConfirm
|
||||
disabled={uiState === 'loading'}
|
||||
align="end"
|
||||
menuItemClassName="danger"
|
||||
confirmLabel="Delete this list?"
|
||||
onClick={() => {
|
||||
const yes = confirm('Delete this list?');
|
||||
if (!yes) return;
|
||||
// const yes = confirm('Delete this list?');
|
||||
// if (!yes) return;
|
||||
setUiState('loading');
|
||||
|
||||
(async () => {
|
||||
|
@ -126,9 +128,15 @@ function ListAddEdit({ list, onClose }) {
|
|||
}
|
||||
})();
|
||||
}}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="light danger"
|
||||
disabled={uiState === 'loading'}
|
||||
>
|
||||
Delete…
|
||||
</button>
|
||||
</MenuConfirm>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
|
|
43
src/components/menu-confirm.jsx
Normal file
43
src/components/menu-confirm.jsx
Normal file
|
@ -0,0 +1,43 @@
|
|||
import { Menu, MenuItem, SubMenu } from '@szhsin/react-menu';
|
||||
import { cloneElement } from 'preact';
|
||||
|
||||
function MenuConfirm({
|
||||
subMenu = false,
|
||||
confirm = true,
|
||||
confirmLabel,
|
||||
menuItemClassName,
|
||||
menuFooter,
|
||||
...props
|
||||
}) {
|
||||
const { children, onClick, ...restProps } = props;
|
||||
if (!confirm) {
|
||||
if (subMenu) return <MenuItem {...props} />;
|
||||
if (onClick) {
|
||||
return cloneElement(children, {
|
||||
onClick,
|
||||
});
|
||||
}
|
||||
return children;
|
||||
}
|
||||
const Parent = subMenu ? SubMenu : Menu;
|
||||
return (
|
||||
<Parent
|
||||
openTrigger="clickOnly"
|
||||
direction="bottom"
|
||||
overflow="auto"
|
||||
gap={-8}
|
||||
shift={8}
|
||||
menuClassName="menu-emphasized"
|
||||
{...restProps}
|
||||
menuButton={subMenu ? undefined : children}
|
||||
label={subMenu ? children : undefined}
|
||||
>
|
||||
<MenuItem className={menuItemClassName} onClick={onClick}>
|
||||
{confirmLabel}
|
||||
</MenuItem>
|
||||
{menuFooter}
|
||||
</Parent>
|
||||
);
|
||||
}
|
||||
|
||||
export default MenuConfirm;
|
|
@ -28,6 +28,7 @@ import { snapshot } from 'valtio/vanilla';
|
|||
import AccountBlock from '../components/account-block';
|
||||
import EmojiText from '../components/emoji-text';
|
||||
import Loader from '../components/loader';
|
||||
import MenuConfirm from '../components/menu-confirm';
|
||||
import Modal from '../components/modal';
|
||||
import NameText from '../components/name-text';
|
||||
import Poll from '../components/poll';
|
||||
|
@ -325,6 +326,12 @@ function Status({
|
|||
};
|
||||
};
|
||||
|
||||
// Check if media has no descriptions
|
||||
const mediaNoDesc = useMemo(() => {
|
||||
return mediaAttachments.some(
|
||||
(attachment) => !attachment.description?.trim?.(),
|
||||
);
|
||||
}, [mediaAttachments]);
|
||||
const boostStatus = async () => {
|
||||
if (!sameInstance || !authenticated) {
|
||||
alert(unauthInteractionErrorMessage);
|
||||
|
@ -332,12 +339,8 @@ function Status({
|
|||
}
|
||||
try {
|
||||
if (!reblogged) {
|
||||
// Check if media has no descriptions
|
||||
const hasNoDescriptions = mediaAttachments.some(
|
||||
(attachment) => !attachment.description?.trim?.(),
|
||||
);
|
||||
let confirmText = 'Boost this post?';
|
||||
if (hasNoDescriptions) {
|
||||
if (mediaNoDesc) {
|
||||
confirmText += '\n\n⚠️ Some media have no descriptions.';
|
||||
}
|
||||
const yes = confirm(confirmText);
|
||||
|
@ -367,6 +370,34 @@ function Status({
|
|||
return false;
|
||||
}
|
||||
};
|
||||
const confirmBoostStatus = async () => {
|
||||
if (!sameInstance || !authenticated) {
|
||||
alert(unauthInteractionErrorMessage);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
// Optimistic
|
||||
states.statuses[sKey] = {
|
||||
...status,
|
||||
reblogged: !reblogged,
|
||||
reblogsCount: reblogsCount + (reblogged ? -1 : 1),
|
||||
};
|
||||
if (reblogged) {
|
||||
const newStatus = await masto.v1.statuses.unreblog(id);
|
||||
saveStatus(newStatus, instance);
|
||||
return true;
|
||||
} else {
|
||||
const newStatus = await masto.v1.statuses.reblog(id);
|
||||
saveStatus(newStatus, instance);
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
// Revert optimistism
|
||||
states.statuses[sKey] = status;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const favouriteStatus = async () => {
|
||||
if (!sameInstance || !authenticated) {
|
||||
|
@ -490,11 +521,27 @@ function Status({
|
|||
{!isSizeLarge && sameInstance && (
|
||||
<>
|
||||
<div class="menu-horizontal">
|
||||
<MenuItem
|
||||
<MenuConfirm
|
||||
subMenu
|
||||
confirmLabel={
|
||||
<>
|
||||
<Icon icon="rocket" />
|
||||
<span>Unboost?</span>
|
||||
</>
|
||||
}
|
||||
menuFooter={
|
||||
mediaNoDesc &&
|
||||
!reblogged && (
|
||||
<div class="footer">
|
||||
<Icon icon="alert" />
|
||||
Some media have no descriptions.
|
||||
</div>
|
||||
)
|
||||
}
|
||||
disabled={!canBoost}
|
||||
onClick={async () => {
|
||||
try {
|
||||
const done = await boostStatus();
|
||||
const done = await confirmBoostStatus();
|
||||
if (!isSizeLarge && done) {
|
||||
showToast(reblogged ? 'Unboosted' : 'Boosted');
|
||||
}
|
||||
|
@ -508,7 +555,7 @@ function Status({
|
|||
}}
|
||||
/>
|
||||
<span>{reblogged ? 'Unboost' : 'Boost…'}</span>
|
||||
</MenuItem>
|
||||
</MenuConfirm>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
try {
|
||||
|
@ -660,10 +707,18 @@ function Status({
|
|||
<span>Edit</span>
|
||||
</MenuItem>
|
||||
{isSizeLarge && (
|
||||
<MenuItem
|
||||
<MenuConfirm
|
||||
subMenu
|
||||
confirmLabel={
|
||||
<>
|
||||
<Icon icon="trash" />
|
||||
<span>Delete this post?</span>
|
||||
</>
|
||||
}
|
||||
menuItemClassName="danger"
|
||||
onClick={() => {
|
||||
const yes = confirm('Delete this post?');
|
||||
if (yes) {
|
||||
// const yes = confirm('Delete this post?');
|
||||
// if (yes) {
|
||||
(async () => {
|
||||
try {
|
||||
await masto.v1.statuses.remove(id);
|
||||
|
@ -675,12 +730,12 @@ function Status({
|
|||
showToast('Unable to delete');
|
||||
}
|
||||
})();
|
||||
}
|
||||
// }
|
||||
}}
|
||||
>
|
||||
<Icon icon="trash" />
|
||||
<span>Delete…</span>
|
||||
</MenuItem>
|
||||
</MenuConfirm>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
@ -1157,7 +1212,7 @@ function Status({
|
|||
onClick={replyStatus}
|
||||
/>
|
||||
</div>
|
||||
<div class="action has-count">
|
||||
{/* <div class="action has-count">
|
||||
<StatusButton
|
||||
checked={reblogged}
|
||||
title={['Boost', 'Unboost']}
|
||||
|
@ -1168,7 +1223,45 @@ function Status({
|
|||
onClick={boostStatus}
|
||||
disabled={!canBoost}
|
||||
/>
|
||||
</div> */}
|
||||
<Menu
|
||||
portal={{
|
||||
target:
|
||||
document.querySelector('.status-deck') || document.body,
|
||||
}}
|
||||
align="start"
|
||||
gap={4}
|
||||
overflow="auto"
|
||||
viewScroll="close"
|
||||
boundingBoxPadding="8 8 8 8"
|
||||
shift={-8}
|
||||
menuClassName="menu-emphasized"
|
||||
menuButton={({ open }) => (
|
||||
<div class="action has-count">
|
||||
<StatusButton
|
||||
checked={reblogged}
|
||||
title={['Boost', 'Unboost']}
|
||||
alt={['Boost', 'Boosted']}
|
||||
class="reblog-button"
|
||||
icon="rocket"
|
||||
count={reblogsCount}
|
||||
// onClick={boostStatus}
|
||||
disabled={open || !canBoost}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<MenuItem onClick={confirmBoostStatus}>
|
||||
<Icon icon="rocket" />
|
||||
<span>Boost to everyone?</span>
|
||||
</MenuItem>
|
||||
{mediaNoDesc && (
|
||||
<div class="footer">
|
||||
<Icon icon="alert" />
|
||||
Some media have no descriptions.
|
||||
</div>
|
||||
)}
|
||||
</Menu>
|
||||
<div class="action has-count">
|
||||
<StatusButton
|
||||
checked={favourited}
|
||||
|
@ -1682,6 +1775,7 @@ function StatusButton({
|
|||
title={buttonTitle}
|
||||
class={`plain ${className} ${checked ? 'checked' : ''}`}
|
||||
onClick={(e) => {
|
||||
if (!onClick) return;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onClick(e);
|
||||
|
|
|
@ -6,6 +6,7 @@ import { useReducer, useState } from 'preact/hooks';
|
|||
import Avatar from '../components/avatar';
|
||||
import Icon from '../components/icon';
|
||||
import Link from '../components/link';
|
||||
import MenuConfirm from '../components/menu-confirm';
|
||||
import NameText from '../components/name-text';
|
||||
import { api } from '../utils/api';
|
||||
import states from '../utils/states';
|
||||
|
@ -126,11 +127,19 @@ function Accounts({ onClose }) {
|
|||
<span>Set as default</span>
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem
|
||||
<MenuConfirm
|
||||
subMenu
|
||||
confirmLabel={
|
||||
<>
|
||||
<Icon icon="exit" />
|
||||
<span>Log out @{account.info.acct}?</span>
|
||||
</>
|
||||
}
|
||||
disabled={!isCurrent}
|
||||
menuItemClassName="danger"
|
||||
onClick={() => {
|
||||
const yes = confirm('Log out?');
|
||||
if (!yes) return;
|
||||
// const yes = confirm('Log out?');
|
||||
// if (!yes) return;
|
||||
accounts.splice(i, 1);
|
||||
store.local.setJSON('accounts', accounts);
|
||||
// location.reload();
|
||||
|
@ -139,7 +148,7 @@ function Accounts({ onClose }) {
|
|||
>
|
||||
<Icon icon="exit" />
|
||||
<span>Log out…</span>
|
||||
</MenuItem>
|
||||
</MenuConfirm>
|
||||
</Menu>
|
||||
</div>
|
||||
</li>
|
||||
|
|
|
@ -10,6 +10,7 @@ import { useNavigate, useParams } from 'react-router-dom';
|
|||
|
||||
import Icon from '../components/icon';
|
||||
import Menu2 from '../components/menu2';
|
||||
import MenuConfirm from '../components/menu-confirm';
|
||||
import Timeline from '../components/timeline';
|
||||
import { api } from '../utils/api';
|
||||
import showToast from '../utils/show-toast';
|
||||
|
@ -149,16 +150,19 @@ function Hashtags({ columnMode, ...props }) {
|
|||
>
|
||||
{!!info && hashtags.length === 1 && (
|
||||
<>
|
||||
<MenuItem
|
||||
<MenuConfirm
|
||||
subMenu
|
||||
confirm={info.following}
|
||||
confirmLabel={`Unfollow #${hashtag}?`}
|
||||
disabled={followUIState === 'loading' || !authenticated}
|
||||
onClick={() => {
|
||||
setFollowUIState('loading');
|
||||
if (info.following) {
|
||||
const yes = confirm(`Unfollow #${hashtag}?`);
|
||||
if (!yes) {
|
||||
setFollowUIState('default');
|
||||
return;
|
||||
}
|
||||
// const yes = confirm(`Unfollow #${hashtag}?`);
|
||||
// if (!yes) {
|
||||
// setFollowUIState('default');
|
||||
// return;
|
||||
// }
|
||||
masto.v1.tags
|
||||
.unfollow(hashtag)
|
||||
.then(() => {
|
||||
|
@ -198,7 +202,7 @@ function Hashtags({ columnMode, ...props }) {
|
|||
<Icon icon="plus" /> <span>Follow</span>
|
||||
</>
|
||||
)}
|
||||
</MenuItem>
|
||||
</MenuConfirm>
|
||||
<MenuDivider />
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -11,6 +11,7 @@ import Icon from '../components/icon';
|
|||
import Link from '../components/link';
|
||||
import ListAddEdit from '../components/list-add-edit';
|
||||
import Menu2 from '../components/menu2';
|
||||
import MenuConfirm from '../components/menu-confirm';
|
||||
import Modal from '../components/modal';
|
||||
import Timeline from '../components/timeline';
|
||||
import { api } from '../utils/api';
|
||||
|
@ -263,10 +264,11 @@ function RemoveAddButton({ account, listID }) {
|
|||
const [removed, setRemoved] = useState(false);
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
class={`light ${removed ? '' : 'danger'}`}
|
||||
disabled={uiState === 'loading'}
|
||||
<MenuConfirm
|
||||
confirm={!removed}
|
||||
confirmLabel={<span>Remove @{account.username} from list?</span>}
|
||||
align="end"
|
||||
menuItemClassName="danger"
|
||||
onClick={() => {
|
||||
if (removed) {
|
||||
setUIState('loading');
|
||||
|
@ -282,8 +284,8 @@ function RemoveAddButton({ account, listID }) {
|
|||
}
|
||||
})();
|
||||
} else {
|
||||
const yes = confirm(`Remove ${account.username} from this list?`);
|
||||
if (!yes) return;
|
||||
// const yes = confirm(`Remove ${account.username} from this list?`);
|
||||
// if (!yes) return;
|
||||
setUIState('loading');
|
||||
|
||||
(async () => {
|
||||
|
@ -299,9 +301,15 @@ function RemoveAddButton({ account, listID }) {
|
|||
})();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class={`light ${removed ? '' : 'danger'}`}
|
||||
disabled={uiState === 'loading'}
|
||||
>
|
||||
{removed ? 'Add' : 'Remove…'}
|
||||
</button>
|
||||
</MenuConfirm>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
34
src/utils/toast-alert.js
Normal file
34
src/utils/toast-alert.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Replace alert() with toastify-js
|
||||
import Toastify from 'toastify-js';
|
||||
|
||||
const nativeAlert = window.alert;
|
||||
if (!window.__nativeAlert) window.__nativeAlert = nativeAlert;
|
||||
|
||||
window.alert = function (message) {
|
||||
console.debug(
|
||||
'ALERT: This is a custom alert() function. Native alert() is still available as window.__nativeAlert()',
|
||||
);
|
||||
// If Error object, show the message
|
||||
if (message instanceof Error && message?.message) {
|
||||
message = message.message;
|
||||
}
|
||||
// If not string, stringify it
|
||||
if (typeof message !== 'string') {
|
||||
message = JSON.stringify(message);
|
||||
}
|
||||
|
||||
const toast = Toastify({
|
||||
text: message,
|
||||
className: 'alert',
|
||||
gravity: 'top',
|
||||
position: 'center',
|
||||
duration: 10_000,
|
||||
offset: {
|
||||
y: 48,
|
||||
},
|
||||
onClick: () => {
|
||||
toast.hideToast();
|
||||
},
|
||||
});
|
||||
toast.showToast();
|
||||
};
|
Loading…
Reference in a new issue