mirror of
https://github.com/cheeaun/phanpy.git
synced 2024-12-25 03:38:15 +03:00
241 lines
7.9 KiB
React
241 lines
7.9 KiB
React
|
import './drafts.css';
|
||
|
|
||
|
import { useEffect, useMemo, useReducer, useState } from 'react';
|
||
|
|
||
|
import db from '../utils/db';
|
||
|
import states from '../utils/states';
|
||
|
import { getCurrentAccountNS } from '../utils/store-utils';
|
||
|
|
||
|
import Icon from './icon';
|
||
|
import Loader from './loader';
|
||
|
|
||
|
function Drafts() {
|
||
|
const [uiState, setUIState] = useState('default');
|
||
|
const [drafts, setDrafts] = useState([]);
|
||
|
const [reloadCount, reload] = useReducer((c) => c + 1, 0);
|
||
|
|
||
|
useEffect(() => {
|
||
|
setUIState('loading');
|
||
|
(async () => {
|
||
|
try {
|
||
|
const keys = await db.drafts.keys();
|
||
|
if (keys.length) {
|
||
|
const ns = getCurrentAccountNS();
|
||
|
const ownKeys = keys.filter((key) => key.startsWith(ns));
|
||
|
if (ownKeys.length) {
|
||
|
const drafts = await db.drafts.getMany(ownKeys);
|
||
|
drafts.sort(
|
||
|
(a, b) =>
|
||
|
new Date(b.updatedAt).getTime() -
|
||
|
new Date(a.updatedAt).getTime(),
|
||
|
);
|
||
|
setDrafts(drafts);
|
||
|
} else {
|
||
|
setDrafts([]);
|
||
|
}
|
||
|
} else {
|
||
|
setDrafts([]);
|
||
|
}
|
||
|
setUIState('default');
|
||
|
} catch (e) {
|
||
|
console.error(e);
|
||
|
setUIState('error');
|
||
|
}
|
||
|
})();
|
||
|
}, [reloadCount]);
|
||
|
|
||
|
const hasDrafts = drafts?.length > 0;
|
||
|
|
||
|
return (
|
||
|
<div class="sheet">
|
||
|
<header>
|
||
|
<h2>
|
||
|
Unsent drafts <Loader abrupt hidden={uiState !== 'loading'} />
|
||
|
</h2>
|
||
|
{hasDrafts && (
|
||
|
<div class="insignificant">
|
||
|
Looks like you have unsent drafts. Let's continue where you left
|
||
|
off.
|
||
|
</div>
|
||
|
)}
|
||
|
</header>
|
||
|
<main>
|
||
|
{hasDrafts ? (
|
||
|
<>
|
||
|
<ul class="drafts-list">
|
||
|
{drafts.map((draft) => {
|
||
|
const { updatedAt, key, draftStatus, replyTo } = draft;
|
||
|
const currentYear = new Date().getFullYear();
|
||
|
const updatedAtDate = new Date(updatedAt);
|
||
|
return (
|
||
|
<li key={updatedAt}>
|
||
|
<div class="mini-draft-meta">
|
||
|
<b>
|
||
|
<Icon icon={replyTo ? 'reply' : 'quill'} size="s" />{' '}
|
||
|
<time>
|
||
|
{!!replyTo && (
|
||
|
<>
|
||
|
@{replyTo.account.acct}
|
||
|
<br />
|
||
|
</>
|
||
|
)}
|
||
|
{Intl.DateTimeFormat('en', {
|
||
|
// Show year if not current year
|
||
|
year:
|
||
|
updatedAtDate.getFullYear() === currentYear
|
||
|
? undefined
|
||
|
: 'numeric',
|
||
|
month: 'short',
|
||
|
day: 'numeric',
|
||
|
weekday: 'short',
|
||
|
hour: 'numeric',
|
||
|
minute: '2-digit',
|
||
|
second: '2-digit',
|
||
|
}).format(updatedAtDate)}
|
||
|
</time>
|
||
|
</b>
|
||
|
<button
|
||
|
type="button"
|
||
|
class="small light"
|
||
|
disabled={uiState === 'loading'}
|
||
|
onClick={() => {
|
||
|
(async () => {
|
||
|
try {
|
||
|
const yes = confirm(
|
||
|
'Are you sure you want to delete this draft?',
|
||
|
);
|
||
|
if (yes) {
|
||
|
await db.drafts.del(key);
|
||
|
reload();
|
||
|
}
|
||
|
} catch (e) {
|
||
|
alert('Error deleting draft! Please try again.');
|
||
|
}
|
||
|
})();
|
||
|
}}
|
||
|
>
|
||
|
Delete…
|
||
|
</button>
|
||
|
</div>
|
||
|
<button
|
||
|
type="button"
|
||
|
disabled={uiState === 'loading'}
|
||
|
class="draft-item"
|
||
|
onClick={async () => {
|
||
|
// console.log({ draftStatus });
|
||
|
let replyToStatus;
|
||
|
if (replyTo) {
|
||
|
setUIState('loading');
|
||
|
try {
|
||
|
replyToStatus = await masto.v1.statuses.fetch(
|
||
|
replyTo.id,
|
||
|
);
|
||
|
} catch (e) {
|
||
|
console.error(e);
|
||
|
alert('Error fetching reply-to status!');
|
||
|
setUIState('default');
|
||
|
return;
|
||
|
}
|
||
|
setUIState('default');
|
||
|
}
|
||
|
window.__COMPOSE__ = {
|
||
|
draftStatus,
|
||
|
replyToStatus,
|
||
|
};
|
||
|
states.showCompose = true;
|
||
|
states.showDrafts = false;
|
||
|
}}
|
||
|
>
|
||
|
<MiniDraft draft={draft} />
|
||
|
</button>
|
||
|
</li>
|
||
|
);
|
||
|
})}
|
||
|
</ul>
|
||
|
<p>
|
||
|
<button
|
||
|
type="button"
|
||
|
class="light danger"
|
||
|
disabled={uiState === 'loading'}
|
||
|
onClick={() => {
|
||
|
(async () => {
|
||
|
const yes = confirm(
|
||
|
'Are you sure you want to delete all drafts?',
|
||
|
);
|
||
|
if (yes) {
|
||
|
setUIState('loading');
|
||
|
try {
|
||
|
await db.drafts.delMany(
|
||
|
drafts.map((draft) => draft.key),
|
||
|
);
|
||
|
setUIState('default');
|
||
|
reload();
|
||
|
} catch (e) {
|
||
|
console.error(e);
|
||
|
alert('Error deleting drafts! Please try again.');
|
||
|
setUIState('error');
|
||
|
}
|
||
|
}
|
||
|
})();
|
||
|
}}
|
||
|
>
|
||
|
Delete all drafts…
|
||
|
</button>
|
||
|
</p>
|
||
|
</>
|
||
|
) : (
|
||
|
<p>No drafts found.</p>
|
||
|
)}
|
||
|
</main>
|
||
|
</div>
|
||
|
);
|
||
|
}
|
||
|
|
||
|
function MiniDraft({ draft }) {
|
||
|
const { draftStatus, replyTo } = draft;
|
||
|
const { status, spoilerText, poll, mediaAttachments } = draftStatus;
|
||
|
const hasPoll = poll?.options?.length > 0;
|
||
|
const hasMedia = mediaAttachments?.length > 0;
|
||
|
const hasPollOrMedia = hasPoll || hasMedia;
|
||
|
const firstImageMedia = useMemo(() => {
|
||
|
if (!hasMedia) return;
|
||
|
const image = mediaAttachments.find((media) => /image/.test(media.type));
|
||
|
if (!image) return;
|
||
|
const { file } = image;
|
||
|
const objectURL = URL.createObjectURL(file);
|
||
|
return objectURL;
|
||
|
}, [hasMedia, mediaAttachments]);
|
||
|
return (
|
||
|
<>
|
||
|
<div class="mini-draft">
|
||
|
{hasPollOrMedia && (
|
||
|
<div
|
||
|
class={`mini-draft-aside ${firstImageMedia ? 'has-image' : ''}`}
|
||
|
style={
|
||
|
firstImageMedia
|
||
|
? {
|
||
|
'--bg-image': `url(${firstImageMedia})`,
|
||
|
}
|
||
|
: {}
|
||
|
}
|
||
|
>
|
||
|
{hasPoll && <Icon icon="poll" />}
|
||
|
{hasMedia && (
|
||
|
<span>
|
||
|
<Icon icon="attachment" />{' '}
|
||
|
<small>{mediaAttachments?.length}</small>
|
||
|
</span>
|
||
|
)}
|
||
|
</div>
|
||
|
)}
|
||
|
<div class="mini-draft-main">
|
||
|
{!!spoilerText && <div class="mini-draft-spoiler">{spoilerText}</div>}
|
||
|
{!!status && <div class="mini-draft-status">{status}</div>}
|
||
|
</div>
|
||
|
</div>
|
||
|
</>
|
||
|
);
|
||
|
}
|
||
|
|
||
|
export default Drafts;
|