mirror of
https://github.com/irislib/iris-messenger.git
synced 2024-10-18 14:13:21 +00:00
HyperText fixes, use RelativeTime
This commit is contained in:
parent
467e37e621
commit
2b65a9a44a
@ -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';
|
||||
|
||||
|
@ -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}</>;
|
||||
},
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
|
@ -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>
|
||||
|
@ -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}>
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
</>
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user