2024-03-21 06:04:32 +00:00
|
|
|
import {
|
|
|
|
Component,
|
|
|
|
ComponentChildren,
|
|
|
|
createRef,
|
|
|
|
Fragment,
|
|
|
|
h,
|
|
|
|
RefObject,
|
|
|
|
} from "https://esm.sh/preact@10.17.1";
|
2024-03-15 13:44:17 +00:00
|
|
|
import { PublicKey } from "../../libs/nostr.ts/key.ts";
|
|
|
|
import { RelayRecordGetter } from "../database.ts";
|
|
|
|
import { emitFunc } from "../event-bus.ts";
|
|
|
|
import { IconButtonClass } from "./components/tw.ts";
|
|
|
|
import { LeftArrowIcon } from "./icons/left-arrow-icon.tsx";
|
|
|
|
import {
|
|
|
|
DirectMessagePanelUpdate,
|
|
|
|
NameAndTime,
|
|
|
|
ParseMessageContent,
|
|
|
|
SyncEvent,
|
|
|
|
Time,
|
|
|
|
ViewUserDetail,
|
|
|
|
} from "./message-panel.tsx";
|
|
|
|
import { ChatMessage, groupContinuousMessages, sortMessage } from "./message.ts";
|
|
|
|
import { ProfileGetter } from "./search.tsx";
|
|
|
|
import { SelectConversation } from "./search_model.ts";
|
|
|
|
import { sleep } from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts";
|
|
|
|
import { ProfileData } from "../features/profile.ts";
|
|
|
|
import { isMobile, setState } from "./_helper.ts";
|
|
|
|
import { Avatar } from "./components/avatar.tsx";
|
|
|
|
import { AboutIcon } from "./icons/about-icon.tsx";
|
|
|
|
import { BackgroundColor_MessagePanel, PrimaryTextColor } from "./style/colors.ts";
|
|
|
|
import { Parsed_Event } from "../nostr.ts";
|
|
|
|
import { NoteID } from "../../libs/nostr.ts/nip19.ts";
|
|
|
|
import { robohash } from "./relay-detail.tsx";
|
2024-03-27 07:38:12 +00:00
|
|
|
import { ReplyIcon } from "./icons/reply-icon.tsx";
|
2024-04-12 09:04:58 +00:00
|
|
|
import { ChatMessagesGetter } from "./app_update.tsx";
|
|
|
|
import { NostrEvent, NostrKind } from "../../libs/nostr.ts/nostr.ts";
|
2024-03-15 13:44:17 +00:00
|
|
|
|
2024-03-21 06:04:32 +00:00
|
|
|
interface Props {
|
2024-03-15 13:44:17 +00:00
|
|
|
myPublicKey: PublicKey;
|
|
|
|
messages: ChatMessage[];
|
2024-04-12 09:04:58 +00:00
|
|
|
emit: emitFunc<
|
|
|
|
DirectMessagePanelUpdate | SelectConversation | SyncEvent | ViewUserDetail | ReplyToMessage
|
|
|
|
>;
|
2024-03-15 13:44:17 +00:00
|
|
|
getters: {
|
2024-04-12 09:04:58 +00:00
|
|
|
messageGetter: ChatMessagesGetter;
|
2024-03-15 13:44:17 +00:00
|
|
|
profileGetter: ProfileGetter;
|
|
|
|
relayRecordGetter: RelayRecordGetter;
|
|
|
|
getEventByID: func_GetEventByID;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
interface MessageListState {
|
|
|
|
offset: number;
|
|
|
|
}
|
|
|
|
|
|
|
|
const ItemsOfPerPage = 50;
|
|
|
|
|
2024-03-21 06:04:32 +00:00
|
|
|
export class MessageList extends Component<Props, MessageListState> {
|
2024-03-15 13:44:17 +00:00
|
|
|
readonly messagesULElement = createRef<HTMLUListElement>();
|
|
|
|
|
|
|
|
state = {
|
|
|
|
offset: 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
jitter = new JitterPrevention(100);
|
|
|
|
|
2024-03-21 06:04:32 +00:00
|
|
|
async componentDidUpdate(previousProps: Readonly<Props>) {
|
2024-03-17 09:00:07 +00:00
|
|
|
const newest = last(this.props.messages);
|
|
|
|
const pre_newest = last(previousProps.messages);
|
|
|
|
if (
|
|
|
|
newest && pre_newest && newest.author.hex == this.props.myPublicKey.hex &&
|
|
|
|
newest.event.id != pre_newest.event.id
|
|
|
|
) {
|
|
|
|
await this.goToLastPage();
|
|
|
|
this.goToButtom(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-15 13:44:17 +00:00
|
|
|
async componentDidMount() {
|
|
|
|
const offset = this.props.messages.length - ItemsOfPerPage;
|
|
|
|
await setState(this, { offset: offset <= 0 ? 0 : offset });
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const messages_to_render = this.sortAndSliceMessage();
|
|
|
|
const groups = groupContinuousMessages(messages_to_render, (pre, cur) => {
|
|
|
|
const sameAuthor = pre.event.pubkey == cur.event.pubkey;
|
|
|
|
const _66sec = Math.abs(cur.created_at.getTime() - pre.created_at.getTime()) <
|
|
|
|
1000 * 60;
|
2024-03-22 04:01:41 +00:00
|
|
|
return sameAuthor && _66sec && !isReply(cur.event);
|
2024-03-15 13:44:17 +00:00
|
|
|
});
|
|
|
|
const messageBoxGroups = [];
|
|
|
|
for (const messages of groups) {
|
2024-04-12 09:04:58 +00:00
|
|
|
const profileEvent = this.props.getters.profileGetter.getProfileByPublicKey(messages[0].author);
|
2024-03-15 13:44:17 +00:00
|
|
|
messageBoxGroups.push(
|
|
|
|
MessageBoxGroup({
|
|
|
|
messages: messages,
|
|
|
|
myPublicKey: this.props.myPublicKey,
|
|
|
|
emit: this.props.emit,
|
|
|
|
authorProfile: profileEvent ? profileEvent.profile : undefined,
|
|
|
|
getters: this.props.getters,
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2024-03-21 06:04:32 +00:00
|
|
|
<div class="w-full flex flex-col overflow-auto">
|
|
|
|
<button class={`${IconButtonClass} shrink-0`} onClick={this.prePage}>
|
|
|
|
load earlier messages
|
|
|
|
</button>
|
|
|
|
{MessageListView(this.goToButtom, this.messagesULElement, messageBoxGroups)}
|
|
|
|
<button class={`${IconButtonClass} shrink-0`} onClick={this.nextPage}>
|
|
|
|
load more messages
|
2024-03-15 13:44:17 +00:00
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
sortAndSliceMessage = () => {
|
|
|
|
return sortMessage(this.props.messages)
|
|
|
|
.slice(
|
|
|
|
this.state.offset,
|
|
|
|
this.state.offset + ItemsOfPerPage,
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
prePage = async () => {
|
|
|
|
const offset = this.state.offset - ItemsOfPerPage / 2;
|
|
|
|
if (offset > 0) {
|
|
|
|
await setState(this, { offset });
|
2024-03-20 08:37:23 +00:00
|
|
|
} else {
|
|
|
|
await setState(this, { offset: 0 });
|
2024-03-15 13:44:17 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
nextPage = async () => {
|
|
|
|
const offset = this.state.offset + ItemsOfPerPage / 2;
|
|
|
|
if (offset < this.props.messages.length) {
|
|
|
|
await setState(this, { offset });
|
2024-03-20 08:37:23 +00:00
|
|
|
} else {
|
|
|
|
await this.goToLastPage();
|
2024-03-15 13:44:17 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-03-17 09:00:07 +00:00
|
|
|
goToButtom = (smooth: boolean) => {
|
2024-03-15 13:44:17 +00:00
|
|
|
if (this.messagesULElement.current) {
|
|
|
|
this.messagesULElement.current.scrollTo({
|
|
|
|
top: this.messagesULElement.current.scrollHeight,
|
|
|
|
left: 0,
|
2024-03-17 09:00:07 +00:00
|
|
|
behavior: smooth ? "smooth" : undefined,
|
2024-03-15 13:44:17 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
2024-03-17 09:00:07 +00:00
|
|
|
|
|
|
|
goToLastPage = async () => {
|
|
|
|
const newOffset = this.props.messages.length - ItemsOfPerPage / 2;
|
|
|
|
await setState(this, {
|
|
|
|
offset: newOffset > 0 ? newOffset : 0,
|
|
|
|
});
|
|
|
|
console.log("goToLastPage", this.state.offset);
|
|
|
|
};
|
2024-03-15 13:44:17 +00:00
|
|
|
}
|
|
|
|
|
2024-03-21 06:04:32 +00:00
|
|
|
function MessageListView(
|
|
|
|
goToButtom: (smooth: boolean) => void,
|
|
|
|
messagesULElement: RefObject<HTMLUListElement>,
|
|
|
|
messageBoxGroups: ComponentChildren,
|
|
|
|
) {
|
|
|
|
return (
|
|
|
|
<div
|
|
|
|
class={`w-full overflow-hidden ${BackgroundColor_MessagePanel}`}
|
|
|
|
style={{
|
|
|
|
transform: "perspective(none)",
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<button
|
|
|
|
onClick={() => goToButtom(true)}
|
|
|
|
class={`${IconButtonClass} fixed z-10 bottom-8 right-4 h-10 w-10 rotate-[-90deg] bg-[#42464D] hover:bg-[#2F3136]`}
|
|
|
|
>
|
|
|
|
<LeftArrowIcon
|
|
|
|
class={`w-6 h-6`}
|
|
|
|
style={{
|
|
|
|
fill: "#F3F4EA",
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</button>
|
|
|
|
<ul
|
|
|
|
class={`w-full h-full overflow-y-auto overflow-x-hidden py-9 mobile:py-2 px-2 mobile:px-0 flex flex-col`}
|
|
|
|
ref={messagesULElement}
|
|
|
|
>
|
|
|
|
{messageBoxGroups}
|
|
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
export class MessageList_V0 extends Component<Props> {
|
2024-03-15 13:44:17 +00:00
|
|
|
readonly messagesULElement = createRef<HTMLUListElement>();
|
|
|
|
|
|
|
|
jitter = new JitterPrevention(100);
|
|
|
|
|
|
|
|
async componentDidMount() {
|
|
|
|
this.goToButtom(false);
|
|
|
|
}
|
|
|
|
|
2024-03-21 06:04:32 +00:00
|
|
|
componentDidUpdate(previousProps: Readonly<Props>): void {
|
2024-03-15 13:44:17 +00:00
|
|
|
// todo: this is not a correct check of if new message is received
|
|
|
|
// a better check is to see if the
|
|
|
|
// current newest message is newer than previous newest message
|
|
|
|
if (previousProps.messages.length < this.props.messages.length) {
|
|
|
|
this.goToButtom(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const messages_to_render = this.sortAndSliceMessage();
|
|
|
|
const groups = groupContinuousMessages(messages_to_render, (pre, cur) => {
|
|
|
|
const sameAuthor = pre.event.pubkey == cur.event.pubkey;
|
|
|
|
const _66sec = Math.abs(cur.created_at.getTime() - pre.created_at.getTime()) <
|
|
|
|
1000 * 60;
|
2024-04-12 09:04:58 +00:00
|
|
|
return sameAuthor && _66sec && !isReply(cur.event);
|
2024-03-15 13:44:17 +00:00
|
|
|
});
|
|
|
|
const messageBoxGroups = [];
|
|
|
|
for (const messages of groups) {
|
|
|
|
const profileEvent = this.props.getters.profileGetter
|
2024-04-12 09:04:58 +00:00
|
|
|
.getProfileByPublicKey(messages[0].author);
|
2024-03-15 13:44:17 +00:00
|
|
|
messageBoxGroups.push(
|
|
|
|
MessageBoxGroup({
|
|
|
|
messages: messages,
|
|
|
|
myPublicKey: this.props.myPublicKey,
|
|
|
|
emit: this.props.emit,
|
|
|
|
authorProfile: profileEvent ? profileEvent.profile : undefined,
|
|
|
|
getters: this.props.getters,
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-03-21 06:04:32 +00:00
|
|
|
return MessageListView(this.goToButtom, this.messagesULElement, messageBoxGroups);
|
2024-03-15 13:44:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sortAndSliceMessage = () => {
|
|
|
|
return sortMessage(this.props.messages);
|
|
|
|
};
|
|
|
|
|
|
|
|
goToButtom = (smooth: boolean) => {
|
|
|
|
if (this.messagesULElement.current) {
|
|
|
|
this.messagesULElement.current.scrollTo({
|
|
|
|
top: this.messagesULElement.current.scrollHeight,
|
|
|
|
left: 0,
|
|
|
|
behavior: smooth ? "smooth" : undefined,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
class JitterPrevention {
|
|
|
|
constructor(private duration: number) {}
|
|
|
|
cancel: ((value: void) => void) | undefined;
|
|
|
|
async shouldExecute(): Promise<boolean> {
|
|
|
|
if (this.cancel) {
|
|
|
|
this.cancel();
|
|
|
|
this.cancel = undefined;
|
|
|
|
return this.shouldExecute();
|
|
|
|
}
|
|
|
|
const p = new Promise<void>((resolve) => {
|
|
|
|
this.cancel = resolve;
|
|
|
|
});
|
|
|
|
const cancelled = await sleep(this.duration, p);
|
|
|
|
return !cancelled;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export type func_GetEventByID = (
|
|
|
|
id: string | NoteID,
|
|
|
|
) => Parsed_Event | undefined;
|
|
|
|
|
|
|
|
function MessageBoxGroup(props: {
|
|
|
|
authorProfile: ProfileData | undefined;
|
|
|
|
messages: ChatMessage[];
|
|
|
|
myPublicKey: PublicKey;
|
|
|
|
emit: emitFunc<
|
2024-04-12 09:04:58 +00:00
|
|
|
DirectMessagePanelUpdate | ViewUserDetail | SelectConversation | SyncEvent | ReplyToMessage
|
2024-03-15 13:44:17 +00:00
|
|
|
>;
|
|
|
|
getters: {
|
2024-04-12 09:04:58 +00:00
|
|
|
messageGetter: ChatMessagesGetter;
|
2024-03-15 13:44:17 +00:00
|
|
|
profileGetter: ProfileGetter;
|
|
|
|
relayRecordGetter: RelayRecordGetter;
|
|
|
|
getEventByID: func_GetEventByID;
|
|
|
|
};
|
|
|
|
}) {
|
|
|
|
const first_message = props.messages[0];
|
|
|
|
const rows = [];
|
2024-03-21 06:04:32 +00:00
|
|
|
|
2024-03-15 13:44:17 +00:00
|
|
|
rows.push(
|
|
|
|
<li
|
2024-03-21 06:04:32 +00:00
|
|
|
class={`px-4 hover:bg-[#32353B] w-full max-w-full flex flex-col pr-8 mobile:pr-4 group relative ${
|
2024-03-15 13:44:17 +00:00
|
|
|
isMobile() ? "select-none" : ""
|
|
|
|
}`}
|
|
|
|
>
|
2024-04-12 09:04:58 +00:00
|
|
|
{MessageActions(first_message, props.emit)}
|
2024-04-01 08:58:35 +00:00
|
|
|
{renderRelply(first_message.event, props.getters, props.emit)}
|
2024-03-21 06:04:32 +00:00
|
|
|
<div class="flex items-start">
|
|
|
|
<Avatar
|
|
|
|
class={`h-8 w-8 mt-[0.45rem] mr-2`}
|
|
|
|
picture={props.authorProfile?.picture ||
|
|
|
|
robohash(first_message.author.hex)}
|
|
|
|
onClick={() => {
|
|
|
|
props.emit({
|
|
|
|
type: "ViewUserDetail",
|
|
|
|
pubkey: first_message.author,
|
|
|
|
});
|
|
|
|
}}
|
|
|
|
/>
|
2024-03-15 13:44:17 +00:00
|
|
|
|
2024-03-21 06:04:32 +00:00
|
|
|
<div
|
|
|
|
class={`flex-1`}
|
|
|
|
style={{
|
|
|
|
maxWidth: "calc(100% - 2.75rem)",
|
|
|
|
}}
|
2024-03-15 13:44:17 +00:00
|
|
|
>
|
2024-03-21 06:04:32 +00:00
|
|
|
{NameAndTime(
|
|
|
|
first_message.author,
|
|
|
|
props.authorProfile,
|
|
|
|
props.myPublicKey,
|
|
|
|
first_message.created_at,
|
|
|
|
)}
|
|
|
|
<pre
|
|
|
|
class={`text-[#DCDDDE] whitespace-pre-wrap break-words font-roboto text-sm`}
|
|
|
|
>
|
|
|
|
{ParseMessageContent(
|
|
|
|
first_message,
|
|
|
|
props.emit,
|
|
|
|
props.getters,
|
|
|
|
)}
|
|
|
|
</pre>
|
|
|
|
</div>
|
2024-03-15 13:44:17 +00:00
|
|
|
</div>
|
|
|
|
</li>,
|
|
|
|
);
|
|
|
|
|
|
|
|
for (let i = 1; i < props.messages.length; i++) {
|
|
|
|
const msg = props.messages[i];
|
|
|
|
rows.push(
|
|
|
|
<li
|
|
|
|
class={`px-4 hover:bg-[#32353B] w-full max-w-full flex items-center pr-8 mobile:pr-4 group relative text-sm ${
|
|
|
|
isMobile() ? "select-none" : ""
|
|
|
|
}`}
|
|
|
|
>
|
2024-04-12 09:04:58 +00:00
|
|
|
{MessageActions(msg, props.emit)}
|
2024-03-15 13:44:17 +00:00
|
|
|
{Time(msg.created_at)}
|
|
|
|
<div
|
|
|
|
class={`flex-1`}
|
|
|
|
style={{
|
|
|
|
maxWidth: "calc(100% - 2.75rem)",
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<pre
|
|
|
|
class={`text-[#DCDDDE] whitespace-pre-wrap break-words font-roboto`}
|
|
|
|
>
|
|
|
|
{ParseMessageContent(msg, props.emit, props.getters)}
|
|
|
|
</pre>
|
|
|
|
</div>
|
|
|
|
</li>,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const vnode = (
|
|
|
|
<ul class={`py-2`}>
|
|
|
|
{rows}
|
|
|
|
</ul>
|
|
|
|
);
|
|
|
|
|
|
|
|
return vnode;
|
|
|
|
}
|
|
|
|
|
2024-04-12 09:04:58 +00:00
|
|
|
export type ReplyToMessage = {
|
|
|
|
type: "ReplyToMessage";
|
|
|
|
event: Parsed_Event;
|
|
|
|
};
|
|
|
|
|
2024-03-15 13:44:17 +00:00
|
|
|
function MessageActions(
|
|
|
|
message: ChatMessage,
|
2024-04-12 09:04:58 +00:00
|
|
|
emit: emitFunc<DirectMessagePanelUpdate | ReplyToMessage>,
|
2024-03-15 13:44:17 +00:00
|
|
|
) {
|
|
|
|
return (
|
|
|
|
<div
|
2024-03-27 07:38:12 +00:00
|
|
|
class={`hidden
|
|
|
|
group-hover:flex
|
|
|
|
h-8
|
|
|
|
bg-[#313338] border-[#27292D] border border-solid rounded
|
|
|
|
hover:drop-shadow-lg
|
|
|
|
absolute top-[-1rem] right-[3rem] `}
|
2024-03-15 13:44:17 +00:00
|
|
|
>
|
2024-04-12 09:04:58 +00:00
|
|
|
<button
|
|
|
|
class={`flex items-center justify-center
|
2024-03-27 07:38:12 +00:00
|
|
|
rounded-l
|
|
|
|
p-1
|
|
|
|
bg-[#313338] hover:bg-[#3A3C41]`}
|
2024-04-12 09:04:58 +00:00
|
|
|
onClick={() => {
|
|
|
|
emit({
|
|
|
|
type: "ReplyToMessage",
|
|
|
|
event: message.event,
|
|
|
|
});
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<ReplyIcon class={`w-5 h-5 text-[#B6BAC0] hover:text-[#D9DBDE]`} />
|
|
|
|
</button>
|
2024-03-27 07:38:12 +00:00
|
|
|
|
2024-03-15 13:44:17 +00:00
|
|
|
<button
|
2024-03-27 07:38:12 +00:00
|
|
|
class={`flex items-center justify-center
|
|
|
|
p-1
|
2024-04-12 09:04:58 +00:00
|
|
|
bg-[#313338] hover:bg-[#3A3C41] rounded-r`}
|
2024-03-15 13:44:17 +00:00
|
|
|
onClick={async () => {
|
|
|
|
emit({
|
|
|
|
type: "ViewEventDetail",
|
|
|
|
message: message,
|
|
|
|
});
|
|
|
|
}}
|
|
|
|
>
|
2024-03-27 07:38:12 +00:00
|
|
|
<AboutIcon class={`w-5 h-5 text-[#B6BAC0] hover:text-[#D9DBDE]`} />
|
2024-03-15 13:44:17 +00:00
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
2024-03-17 09:00:07 +00:00
|
|
|
|
|
|
|
function last<T>(array: Array<T>): T | undefined {
|
|
|
|
if (array.length == 0) {
|
|
|
|
return undefined;
|
|
|
|
} else {
|
|
|
|
return array[array.length - 1];
|
|
|
|
}
|
|
|
|
}
|
2024-03-21 06:04:32 +00:00
|
|
|
|
2024-03-22 04:01:41 +00:00
|
|
|
function isReply(event: Parsed_Event) {
|
|
|
|
return event.parsedTags.reply || event.parsedTags.root || event.parsedTags.e.length != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
function renderRelply(event: Parsed_Event, getters: {
|
2024-04-12 09:04:58 +00:00
|
|
|
messageGetter: ChatMessagesGetter;
|
2024-03-22 04:01:41 +00:00
|
|
|
getEventByID: func_GetEventByID;
|
|
|
|
profileGetter: ProfileGetter;
|
2024-04-01 08:58:35 +00:00
|
|
|
}, emit: emitFunc<ViewUserDetail>) {
|
2024-03-22 04:01:41 +00:00
|
|
|
if (!isReply(event)) return;
|
|
|
|
const replyEventId = event.parsedTags.reply?.[0] || event.parsedTags.root?.[0] || event.parsedTags.e[0];
|
|
|
|
const reply_to_event = getters.getEventByID(replyEventId);
|
|
|
|
if (!reply_to_event) {
|
|
|
|
return <ReplyTo unknown noteId={NoteID.FromString(replyEventId)} />;
|
|
|
|
}
|
|
|
|
let author = reply_to_event.publicKey.bech32();
|
|
|
|
let picture = robohash(reply_to_event.publicKey.hex);
|
|
|
|
if (reply_to_event.pubkey) {
|
2024-04-12 09:04:58 +00:00
|
|
|
const profile = getters.profileGetter.getProfileByPublicKey(reply_to_event.publicKey);
|
2024-03-22 04:01:41 +00:00
|
|
|
if (profile) {
|
|
|
|
author = profile.profile.name || profile.profile.display_name ||
|
|
|
|
reply_to_event?.publicKey.bech32();
|
|
|
|
picture = profile.profile.picture || robohash(reply_to_event.publicKey.hex);
|
|
|
|
}
|
|
|
|
}
|
2024-04-12 09:04:58 +00:00
|
|
|
let content = reply_to_event.content;
|
|
|
|
if (reply_to_event.kind === NostrKind.DIRECT_MESSAGE) {
|
|
|
|
const message = getters.messageGetter.getMessageById(reply_to_event.id);
|
|
|
|
if (message) content = message.content;
|
|
|
|
}
|
2024-04-01 08:58:35 +00:00
|
|
|
return (
|
|
|
|
<ReplyTo
|
|
|
|
emit={emit}
|
|
|
|
reply={{
|
|
|
|
pubkey: reply_to_event.publicKey,
|
2024-04-12 09:04:58 +00:00
|
|
|
content,
|
2024-04-01 08:58:35 +00:00
|
|
|
name: author,
|
|
|
|
picture,
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
);
|
2024-03-22 04:01:41 +00:00
|
|
|
}
|
|
|
|
|
2024-03-21 06:04:32 +00:00
|
|
|
function ReplyTo(
|
2024-04-01 08:58:35 +00:00
|
|
|
props: {
|
|
|
|
unknown?: false;
|
|
|
|
reply: {
|
|
|
|
pubkey: PublicKey;
|
|
|
|
content: string;
|
|
|
|
name: string;
|
|
|
|
picture: string;
|
|
|
|
};
|
|
|
|
emit: emitFunc<ViewUserDetail>;
|
|
|
|
} | {
|
2024-03-21 06:04:32 +00:00
|
|
|
unknown: true;
|
|
|
|
noteId: NoteID;
|
|
|
|
},
|
|
|
|
) {
|
|
|
|
return (
|
|
|
|
<div class="w-full flex flex-row">
|
|
|
|
<div class="w-10 h-5 shrink-0">
|
|
|
|
<div class="w-5 h-2.5 border-l-2 border-t-2 rounded-tl translate-y-2.5 translate-x-4 border-[#4F5058]" />
|
|
|
|
</div>
|
|
|
|
<div class="flex flex-row w-full justify-start items-center text-[#A3A6AA] gap-2 font-roboto text-sm pr-5">
|
|
|
|
{props.unknown
|
|
|
|
? (
|
|
|
|
<div class="overflow-hidden whitespace-nowrap text-overflow-ellipsis">
|
|
|
|
{props.noteId.bech32()}
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
: (
|
|
|
|
<>
|
2024-04-01 08:58:35 +00:00
|
|
|
<div
|
|
|
|
class={`flex items-center gap-1 cursor-pointer`}
|
|
|
|
onClick={() =>
|
|
|
|
props.emit({
|
|
|
|
type: "ViewUserDetail",
|
|
|
|
pubkey: props.reply.pubkey,
|
|
|
|
})}
|
|
|
|
>
|
|
|
|
<Avatar
|
|
|
|
class="h-4 w-4 shrink-0"
|
|
|
|
picture={props.reply.picture || ""}
|
|
|
|
/>
|
|
|
|
<button class="whitespace-nowrap md:shrink-0 truncate w-30 hover:underline">
|
|
|
|
@{props.reply.name}
|
|
|
|
</button>
|
2024-03-21 06:04:32 +00:00
|
|
|
</div>
|
|
|
|
<div class="overflow-hidden whitespace-nowrap truncate text-overflow-ellipsis w-[90%]">
|
2024-04-01 08:58:35 +00:00
|
|
|
{props.reply.content}
|
2024-03-21 06:04:32 +00:00
|
|
|
</div>
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|