HyperText fixes, use RelativeTime

This commit is contained in:
Martti Malmi 2023-08-06 08:47:30 +03:00
parent 467e37e621
commit 2b65a9a44a
9 changed files with 44 additions and 71 deletions

View File

@ -5,7 +5,7 @@ import _ from 'lodash';
import throttle from 'lodash/throttle';
import Key from './nostr/Key';
import { language, translate as t } from './translations/Translation.mjs';
import { language } from './translations/Translation.mjs';
import localState from './LocalState';
const emojiRegex =
@ -235,35 +235,6 @@ export default {
}
},
getRelativeTimeText(date: Date): string {
const currentTime = new Date();
const timeDifference = Math.floor((currentTime.getTime() - date.getTime()) / 1000);
const secondsInAMinute = 60;
const secondsInAnHour = 60 * secondsInAMinute;
const secondsInADay = 24 * secondsInAnHour;
if (timeDifference < secondsInAMinute) {
return t('now');
} else if (timeDifference < secondsInAnHour) {
return Math.floor(timeDifference / secondsInAMinute) + 'm';
} else if (timeDifference < secondsInADay) {
return Math.floor(timeDifference / secondsInAnHour) + 'h';
} else {
if (date.getFullYear() === currentTime.getFullYear()) {
return date.toLocaleDateString(undefined, {
month: 'short',
day: 'numeric',
});
} else {
return date.toLocaleDateString(undefined, {
year: 'numeric',
month: 'short',
day: 'numeric',
});
}
}
},
formatBytes(bytes: number, decimals = 2): string {
if (bytes === 0) return '0 Bytes';

View File

@ -11,7 +11,7 @@ localState.get('settings').on((s) => (settings = s));
const HyperText = memo(
({ children, event, textOnly }: { children: string; event?: Event; textOnly?: boolean }) => {
let processedChildren = [children.trim()];
let processedChildren = [children?.trim()] as any[];
const embeds = textOnly ? textEmbeds : allEmbeds;
@ -27,9 +27,19 @@ const HyperText = memo(
});
});
processedChildren = processedChildren.map((x) =>
typeof x === 'string' ? x.replace(/^\n+|\n+$/g, '') : x,
);
processedChildren = processedChildren.map((x, index, array) => {
if (typeof x === 'string') {
console.log(' array[index + 1]', array[index + 1]);
if (index < array.length - 1 && !array[index + 1].props?.href) {
x = x.replace(/\n+$/, ''); // Remove trailing newlines if next element is not a link
}
if (index > 0 && !array[index - 1].props?.href) {
x = x.replace(/^\n+/, ''); // Remove leading newlines if previous element is not a link
}
}
return x;
});
return <>{processedChildren}</>;
},

View File

@ -10,6 +10,7 @@ import { DecryptedEvent } from '../views/chat/ChatMessages';
import Name from './user/Name';
import HyperText from './HyperText';
import RelativeTime from './RelativeTime';
import Torrent from './Torrent';
type Props = {
@ -84,9 +85,6 @@ const PrivateMessage = ({ event, selfAuthored, showName, torrentId }: Props) =>
const emojiOnly = text && text.length === 2 && Helpers.isEmoji(text);
// TODO opts.onImageClick show image in modal
const time =
typeof event.created_at === 'object' ? event.created_at : new Date(event.created_at * 1000);
const status: any = ''; // this.getSeenStatus();
const seen = status.seen ? 'text-green-500' : 'text-neutral-500';
const delivered = status.delivered ? 'border-green-500' : 'border-neutral-500';
@ -111,7 +109,7 @@ const PrivateMessage = ({ event, selfAuthored, showName, torrentId }: Props) =>
<HyperText event={event}>{text}</HyperText>
</div>
<div className={`${selfAuthored ? 'text-right' : 'text-left'} text-xs text-white`}>
{event.id ? Helpers.getRelativeTimeText(time) : Helpers.formatTime(time)}
<RelativeTime date={new Date(event.created_at * 1000)} />
</div>
</div>
</div>

View File

@ -1,4 +1,4 @@
const t = (key: string) => key; // TODO translate
import { translate as t } from '../translations/Translation.mjs';
export default function RelativeTime({ date }: { date: Date }) {
const currentTime = new Date();

View File

@ -1,9 +1,9 @@
import { useMemo } from 'react';
import { Link } from 'preact-router';
import Helpers from '../../../Helpers';
import Key from '../../../nostr/Key';
import Show from '../../helpers/Show';
import RelativeTime from '../../RelativeTime';
import Name from '../../user/Name';
import EventDropdown from '../EventDropdown';
@ -42,7 +42,7 @@ const Author = ({ event, fullWidth, isQuote, standalone, setTranslatedText }) =>
className="tooltip"
data-tip={`${dateStr} ${timeStr}`}
>
{time && Helpers.getRelativeTimeText(time)}
<RelativeTime date={time} />
</Link>
</small>
</div>

View File

@ -199,7 +199,7 @@ const ProfileCard = (props: { hexPub: string; npub: string }) => {
<Stats address={hexPub} />
<div className="py-2">
<p className="text-sm">
<HyperText textOnly={true}>{profile.about.slice(0, 500)}</HyperText>
<HyperText textOnly={true}>{profile.about?.slice(0, 500)}</HyperText>
</p>
<div className="flex flex-1 flex-row align-center justify-center mt-4">
<Show when={lightning}>

View File

@ -416,14 +416,12 @@ const Events = {
// also save to localState, so we don't have to decrypt every time?
const innerEvent = JSON.parse(decrypted.slice(decrypted.indexOf('{')));
if (validateEvent(innerEvent) && verifySignature(innerEvent)) {
console.log('innerEvent', innerEvent);
// parse nsec from message by regex. nsec is bech32 encoded in the message
// no follow distance check here for now
const nsec = innerEvent.content.match(/nsec1[023456789acdefghjklmnpqrstuvwxyz]{6,}/gi)?.[0];
if (nsec) {
const hexPriv = Key.toNostrHexAddress(nsec);
if (hexPriv) {
console.log('nsec & hexpriv');
// TODO browser notification?
addGroup(hexPriv, false, innerEvent.pubkey); // for some reason, groups don't appear on 1st load after login
setGroupNameByInvite(hexPriv, innerEvent.pubkey);

View File

@ -2,9 +2,9 @@ import { memo } from 'react';
import { route } from 'preact-router';
import Show from '../../components/helpers/Show';
import RelativeTime from '../../components/RelativeTime';
import Avatar from '../../components/user/Avatar';
import Name from '../../components/user/Name';
import Helpers from '../../Helpers';
import Key from '../../nostr/Key';
import { translate as t } from '../../translations/Translation.mjs';
@ -16,10 +16,6 @@ const ChatListItem = ({ chat, active = false, latestMsg = {} as any, name }) =>
}
};
const time =
(latestMsg.created_at && Helpers.getRelativeTimeText(new Date(latestMsg.created_at * 1000))) ||
'';
const npub = Key.toNostrBech32Address(chat, 'npub');
return (
@ -42,7 +38,9 @@ const ChatListItem = ({ chat, active = false, latestMsg = {} as any, name }) =>
<Show when={chat !== Key.getPubKey()}>
<Name pub={chat} />
</Show>
<small className="ml-2 latest-time text-neutral-500">{time}</small>
<small className="ml-2 latest-time text-neutral-500">
<RelativeTime date={new Date(latestMsg.created_at * 1000)} />
</small>
</Show>
</span>
<small className="text-neutral-500 truncate">{latestMsg.text}</small>

View File

@ -208,24 +208,6 @@ function ChatMessages({ id }) {
return mainView;
};
const renderMsgForm = () => {
return (
<>
<div className="relative">
<div
id="scroll-down-btn"
style={{ display: 'none' }}
className="absolute bottom-1 left-2 p-1.5 rounded-3xl bg-neutral-900 opacity-85 hover:cursor-pointer"
onClick={() => scrollDown()}
>
<ChevronDownIcon width="24" />
</div>
</div>
<ChatMessageForm key={id} activeChat={id} onSubmit={() => scrollDown()} keyPair={keyPair} />
</>
);
};
// on ref.current height change scroll down. TODO only if stickToBottom
useLayoutEffect(() => {
const el = ref.current;
@ -298,7 +280,6 @@ function ChatMessages({ id }) {
if (el) {
el.off('scroll').on('scroll', () => {
const scrolledToBottom = el.scrollTop() + el.innerHeight() >= el[0].scrollHeight;
console.log('scrolledToBottom', scrolledToBottom);
if (stickToBottom && !scrolledToBottom) {
setStickToBottom(false);
} else if (!stickToBottom && scrolledToBottom) {
@ -325,11 +306,28 @@ function ChatMessages({ id }) {
return (
<>
<Helmet>
<title>{'Messages'}</title>
<title>{t('messages')}</title>
</Helmet>
<div className={`${id ? '' : 'hidden'} flex flex-1 flex-col`}>
{renderMainView()}
<Show when={id && id.length > 4}>{renderMsgForm()}</Show>
<Show when={id && id.length > 4}>
<div className="relative">
<div
id="scroll-down-btn"
style={{ display: 'none' }}
className="absolute bottom-1 left-2 p-1.5 rounded-3xl bg-neutral-900 opacity-85 hover:cursor-pointer"
onClick={() => scrollDown()}
>
<ChevronDownIcon width="24" />
</div>
</div>
<ChatMessageForm
key={id}
activeChat={id}
onSubmit={() => scrollDown()}
keyPair={keyPair}
/>
</Show>
</div>
</>
);