diff --git a/src/components/drafts.jsx b/src/components/drafts.jsx
index f47217d1..be783f15 100644
--- a/src/components/drafts.jsx
+++ b/src/components/drafts.jsx
@@ -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)}
-
+
+
{editMode && (
- {
- const yes = confirm('Delete this list?');
- if (!yes) return;
+ // const yes = confirm('Delete this list?');
+ // if (!yes) return;
setUiState('loading');
(async () => {
@@ -127,8 +129,14 @@ function ListAddEdit({ list, onClose }) {
})();
}}
>
- Delete…
-
+
+ Delete…
+
+
)}
diff --git a/src/components/menu-confirm.jsx b/src/components/menu-confirm.jsx
new file mode 100644
index 00000000..14e67746
--- /dev/null
+++ b/src/components/menu-confirm.jsx
@@ -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 ;
+ if (onClick) {
+ return cloneElement(children, {
+ onClick,
+ });
+ }
+ return children;
+ }
+ const Parent = subMenu ? SubMenu : Menu;
+ return (
+
+
+ {menuFooter}
+
+ );
+}
+
+export default MenuConfirm;
diff --git a/src/components/status.jsx b/src/components/status.jsx
index f77bf690..e926fb38 100644
--- a/src/components/status.jsx
+++ b/src/components/status.jsx
@@ -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 && (
<>
-
+
{isSizeLarge && (
-
+
)}
)}
@@ -1157,7 +1212,7 @@ function Status({
onClick={replyStatus}
/>
-
+ {/*
-
+
*/}
+
{
+ if (!onClick) return;
e.preventDefault();
e.stopPropagation();
onClick(e);
diff --git a/src/pages/accounts.jsx b/src/pages/accounts.jsx
index b5762077..c7910b5a 100644
--- a/src/pages/accounts.jsx
+++ b/src/pages/accounts.jsx
@@ -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 }) {
Set as default
)}
-
+
diff --git a/src/pages/hashtag.jsx b/src/pages/hashtag.jsx
index b81a6744..d9a91564 100644
--- a/src/pages/hashtag.jsx
+++ b/src/pages/hashtag.jsx
@@ -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 && (
<>
-
+
>
)}
diff --git a/src/pages/list.jsx b/src/pages/list.jsx
index 3c360288..dcfa71e4 100644
--- a/src/pages/list.jsx
+++ b/src/pages/list.jsx
@@ -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 (
- Remove @{account.username} from list?}
+ 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 () => {
@@ -300,8 +302,14 @@ function RemoveAddButton({ account, listID }) {
}
}}
>
- {removed ? 'Add' : 'Remove…'}
-
+
+ {removed ? 'Add' : 'Remove…'}
+
+
);
}
diff --git a/src/utils/toast-alert.js b/src/utils/toast-alert.js
new file mode 100644
index 00000000..d4014775
--- /dev/null
+++ b/src/utils/toast-alert.js
@@ -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();
+};