1
0
Fork 0

Add "Edited at" meta with Edit History modal

Much refactor, kinda ugly code still.

Edit History design is still very basic.
This commit is contained in:
Lim Chee Aun 2022-12-11 21:22:22 +08:00
parent 6f3eae15b6
commit cb64f5ffda
6 changed files with 329 additions and 140 deletions

View file

@ -291,11 +291,23 @@ a.hashtag {
.box { .box {
width: 40em; width: 40em;
max-width: 100vw; max-width: 100vw;
text-align: center;
padding: 16px; padding: 16px;
background-color: var(--bg-color); background-color: var(--bg-color);
border-radius: 8px; border-radius: 8px;
border: 1px solid var(--divider-color); border: 1px solid var(--divider-color);
overflow: auto;
max-height: 90vh;
position: relative;
}
.box > :is(h1, h2, h3):first-of-type {
margin-top: 0;
}
.box .close-button {
position: sticky;
top: 0;
float: right;
margin: -16px -8px 0 0;
transform: translate(0, -8px);
} }
.box-shadow { .box-shadow {

View file

@ -91,7 +91,7 @@
color: var(--reply-to-color); color: var(--reply-to-color);
vertical-align: middle; vertical-align: middle;
} }
.status > .container > .meta .time { .status > .container > .meta :is(.time, .edited) {
color: inherit; color: inherit;
text-align: end; text-align: end;
opacity: 0.5; opacity: 0.5;
@ -369,6 +369,32 @@ a.card:hover {
margin: 8px 0; margin: 8px 0;
} }
/* EXTRA META */
.status .extra-meta {
padding-top: 8px;
color: var(--text-insignificant-color);
}
.status .extra-meta * {
vertical-align: middle;
}
.status .extra-meta a {
color: inherit;
text-decoration: none;
}
.status .extra-meta a:hover {
text-decoration: underline;
}
.status .extra-meta .edited:hover {
cursor: pointer;
color: var(--text-color);
}
.status.large .extra-meta {
padding-top: 0;
margin-left: calc(-50px - 16px);
background-color: var(--bg-color);
}
/* ACTIONS */ /* ACTIONS */
.status .actions { .status .actions {
@ -451,3 +477,21 @@ a.card:hover {
vertical-align: middle; vertical-align: middle;
object-fit: contain; object-fit: contain;
} }
/* EDIT HISTORY */
#edit-history {
min-height: 50vh;
min-height: 50dvh;
}
#edit-history :is(ol, ol li){
list-style: none;
margin: 0;
padding: 0;
}
#edit-history .history-item .status {
border: 1px solid var(--outline-color);
border-radius: 8px;
}

View file

@ -6,6 +6,7 @@ import { useEffect, useRef, useState } from 'preact/hooks';
import { InView } from 'react-intersection-observer'; import { InView } from 'react-intersection-observer';
import { useSnapshot } from 'valtio'; import { useSnapshot } from 'valtio';
import Loader from '../components/loader';
import Modal from '../components/modal'; import Modal from '../components/modal';
import NameText from '../components/name-text'; import NameText from '../components/name-text';
import enhanceContent from '../utils/enhance-content'; import enhanceContent from '../utils/enhance-content';
@ -375,12 +376,86 @@ function Poll({ poll }) {
); );
} }
function EditedAtModal({ statusID, onClose = () => {} }) {
const [uiState, setUIState] = useState('default');
const [editHistory, setEditHistory] = useState([]);
useEffect(() => {
setUIState('loading');
(async () => {
try {
const editHistory = await masto.statuses.fetchHistory(statusID);
console.log(editHistory);
setEditHistory(editHistory);
setUIState('default');
} catch (e) {
console.error(e);
setUIState('error');
}
})();
}, []);
const currentYear = new Date().getFullYear();
return (
<div id="edit-history" class="box">
<button type="button" class="close-button plain large" onClick={onClose}>
<Icon icon="x" alt="Close" />
</button>
<h2>Edit History</h2>
{uiState === 'error' && <p>Failed to load history</p>}
{uiState === 'loading' && (
<p>
<Loader abrupt /> Loading&hellip;
</p>
)}
{editHistory.length > 0 && (
<ol>
{editHistory.map((status) => {
const { createdAt } = status;
const createdAtDate = new Date(createdAt);
return (
<li key={createdAt} class="history-item">
<h3>
<time>
{Intl.DateTimeFormat('en', {
// Show year if not current year
year:
createdAtDate.getFullYear() === currentYear
? undefined
: 'numeric',
month: 'short',
day: 'numeric',
weekday: 'short',
hour: 'numeric',
minute: '2-digit',
second: '2-digit',
}).format(createdAtDate)}
</time>
</h3>
<Status status={status} size="s" withinContext editStatus />
</li>
);
})}
</ol>
)}
</div>
);
}
function fetchAccount(id) { function fetchAccount(id) {
return masto.accounts.fetch(id); return masto.accounts.fetch(id);
} }
const memFetchAccount = mem(fetchAccount); const memFetchAccount = mem(fetchAccount);
function Status({ statusID, status, withinContext, size = 'm', skeleton }) { function Status({
statusID,
status,
withinContext,
size = 'm',
skeleton,
editStatus,
}) {
if (skeleton) { if (skeleton) {
return ( return (
<div class="status skeleton"> <div class="status skeleton">
@ -443,8 +518,9 @@ function Status({ statusID, status, withinContext, size = 'm', skeleton }) {
} = status; } = status;
const createdAtDate = new Date(createdAt); const createdAtDate = new Date(createdAt);
const editedAtDate = new Date(editedAt);
let inReplyToAccountRef = mentions.find( let inReplyToAccountRef = mentions?.find(
(mention) => mention.id === inReplyToAccountId, (mention) => mention.id === inReplyToAccountId,
); );
if (!inReplyToAccountRef && inReplyToAccountId === id) { if (!inReplyToAccountRef && inReplyToAccountId === id) {
@ -498,8 +574,10 @@ function Status({ statusID, status, withinContext, size = 'm', skeleton }) {
} }
const [actionsUIState, setActionsUIState] = useState(null); // boost-loading, favourite-loading, bookmark-loading const [actionsUIState, setActionsUIState] = useState(null); // boost-loading, favourite-loading, bookmark-loading
const [showEdited, setShowEdited] = useState(false);
const carouselRef = useRef(null); const carouselRef = useRef(null);
const currentYear = new Date().getFullYear();
return ( return (
<div <div
@ -546,6 +624,7 @@ function Status({ statusID, status, withinContext, size = 'm', skeleton }) {
</> </>
)} )}
</span>{' '} </span>{' '}
{size !== 'l' && !editStatus && (
<a href={uri} target="_blank" class="time"> <a href={uri} target="_blank" class="time">
<Icon <Icon
icon={visibilityIconsMap[visibility]} icon={visibilityIconsMap[visibility]}
@ -561,6 +640,7 @@ function Status({ statusID, status, withinContext, size = 'm', skeleton }) {
{createdAtDate.toLocaleString()} {createdAtDate.toLocaleString()}
</relative-time> </relative-time>
</a> </a>
)}
</div> </div>
<div <div
class={`content-container ${ class={`content-container ${
@ -593,7 +673,6 @@ function Status({ statusID, status, withinContext, size = 'm', skeleton }) {
if (target.parentNode.tagName.toLowerCase() === 'a') { if (target.parentNode.tagName.toLowerCase() === 'a') {
target = target.parentNode; target = target.parentNode;
} }
console.log('click', target, e);
if ( if (
target.tagName.toLowerCase() === 'a' && target.tagName.toLowerCase() === 'a' &&
target.classList.contains('u-url') target.classList.contains('u-url')
@ -662,6 +741,50 @@ function Status({ statusID, status, withinContext, size = 'm', skeleton }) {
)} )}
</div> </div>
{size === 'l' && ( {size === 'l' && (
<>
<div class="extra-meta">
<Icon icon={visibilityIconsMap[visibility]} alt={visibility} />{' '}
<a href={uri} target="_blank">
<time class="created" datetime={createdAtDate.toISOString()}>
{Intl.DateTimeFormat('en', {
// Show year if not current year
year:
createdAtDate.getFullYear() === currentYear
? undefined
: 'numeric',
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: '2-digit',
}).format(createdAtDate)}
</time>
</a>
{editedAt && (
<>
{' '}
&bull; <Icon icon="pencil" alt="Edited" />{' '}
<time
class="edited"
datetime={editedAtDate.toISOString()}
onClick={() => {
setShowEdited(id);
}}
>
{Intl.DateTimeFormat('en', {
// Show year if not this year
year:
editedAtDate.getFullYear() === currentYear
? undefined
: 'numeric',
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: '2-digit',
}).format(editedAtDate)}
</time>
</>
)}
</div>
<div class="actions"> <div class="actions">
<button <button
type="button" type="button"
@ -734,7 +857,9 @@ function Status({ statusID, status, withinContext, size = 'm', skeleton }) {
<button <button
type="button" type="button"
title={favourited ? 'Unfavourite' : 'Favourite'} title={favourited ? 'Unfavourite' : 'Favourite'}
class={`plain favourite-button ${favourited ? 'favourited' : ''}`} class={`plain favourite-button ${
favourited ? 'favourited' : ''
}`}
disabled={actionsUIState === 'favourite-loading'} disabled={actionsUIState === 'favourite-loading'}
onClick={async (e) => { onClick={async (e) => {
e.preventDefault(); e.preventDefault();
@ -771,7 +896,9 @@ function Status({ statusID, status, withinContext, size = 'm', skeleton }) {
<button <button
type="button" type="button"
title={bookmarked ? 'Unbookmark' : 'Bookmark'} title={bookmarked ? 'Unbookmark' : 'Bookmark'}
class={`plain bookmark-button ${bookmarked ? 'bookmarked' : ''}`} class={`plain bookmark-button ${
bookmarked ? 'bookmarked' : ''
}`}
disabled={actionsUIState === 'bookmark-loading'} disabled={actionsUIState === 'bookmark-loading'}
onClick={async (e) => { onClick={async (e) => {
e.preventDefault(); e.preventDefault();
@ -800,6 +927,7 @@ function Status({ statusID, status, withinContext, size = 'm', skeleton }) {
/> />
</button> </button>
</div> </div>
</>
)} )}
</div> </div>
{showMediaModal !== false && ( {showMediaModal !== false && (
@ -908,6 +1036,22 @@ function Status({ statusID, status, withinContext, size = 'm', skeleton }) {
)} )}
</Modal> </Modal>
)} )}
{!!showEdited && (
<Modal
onClick={(e) => {
if (e.target === e.currentTarget) {
setShowEdited(false);
}
}}
>
<EditedAtModal
statusID={showEdited}
onClose={() => {
setShowEdited(false);
}}
/>
</Modal>
)}
</div> </div>
); );
} }

View file

@ -53,7 +53,7 @@ export default () => {
}; };
return ( return (
<main class="box"> <main class="box" style={{ textAlign: 'center' }}>
<form onSubmit={onSubmit}> <form onSubmit={onSubmit}>
<h1>Log in</h1> <h1>Log in</h1>
<label> <label>

View file

@ -1,16 +1,6 @@
#settings-container { #settings-container {
text-align: left;
padding-bottom: 3em; padding-bottom: 3em;
animation: fade-in 0.2s ease-out; animation: fade-in 0.2s ease-out;
max-height: 100vh;
overflow: auto;
position: relative;
}
#settings-container .close-button {
position: absolute;
top: 0;
right: 0;
} }
#settings-container h2 { #settings-container h2 {

View file

@ -1,6 +1,5 @@
#welcome { #welcome {
overflow: auto; text-align: center;
max-height: 90vh;
} }
#welcome img { #welcome img {