Skip to content

Commit

Permalink
.
Browse files Browse the repository at this point in the history
  • Loading branch information
lil5 committed May 13, 2024
1 parent 852b1f2 commit 0716c8f
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 105 deletions.
101 changes: 57 additions & 44 deletions app/src/components/Chat/ChatPost.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,69 @@
import { IonItem } from "@ionic/react";
import { Post } from "@mattermost/types/posts";
import { User } from "../../api/types";
import { useMemo } from "react";
import { useMemo, useState } from "react";
import { UserProfile } from "@mattermost/types/users";
import { useLongPress } from "use-long-press";
import { IonButton, IonIcon } from "@ionic/react";
import { ellipsisHorizontal } from "ionicons/icons";

interface Props {
export interface ChatPostProps {
post: Post;
isMe: boolean;
users: User[];
prevPost: Post | null;
authUser: User | null | undefined;
getMmUser: (id: string) => Promise<UserProfile>;
isChainAdmin: boolean;
onLongPress: (id: string) => void;
}

export default function ChatPost({ post, isMe, users, prevPost }: Props) {
const { username, message } = useMemo(() => {
let username = post.props.username;
let message = post.message;
for (let user of users) {
if (user.uid === post.props.username) {
username = user.name;
}
export default function ChatPost(props: ChatPostProps) {
const longPress = useLongPress((e) => {
props.onLongPress(props.post.id);
});
const [username, setUsername] = useState("");
const [isMe, setIsMe] = useState(false);
const message = useMemo(() => {
props.getMmUser(props.post.user_id).then((res) => {
const user = props.users.find((u) => res.username === u.uid);
setUsername(user ? user.name : res.username);
setIsMe(user?.uid === props.authUser?.uid);
});

let message = props.post.message;
for (let user of props.users) {
message = message.replaceAll(user.uid, user.name);
}
return { username, message };
}, [users, post]);

const isSameUserAsPrev = post.user_id === prevPost?.user_id;
return message;
}, [props.users, props.post]);

return (
<>
{!isSameUserAsPrev ? (
<div className="tw-sticky tw-top-0 tw-font-bold tw-text-center tw-bg-[#ffffffa0]">
{username}
</div>
) : null}
{post.type != "" ? (
<div className="tw-flex tw-justify-center">
<div className="tw-rounded tw-bg-light tw-max-w-xs tw-text-center tw-p-1 tw-my-2">
{message}
</div>
</div>
) : (
<div className={` tw-shrink-0 ${post.is_following ? "" : "tw-mb-2"} `}>
<div
className={`tw-rounded-tl-2xl tw-rounded-tr-2xl tw-bg-light tw-p-2 ${
isMe
? "tw-rounded-bl-2xl tw-float-right tw-ml-8 tw-mr-4"
: "tw-rounded-br-2xl tw-mr-8 tw-ml-4 tw-float-left"
}`}
>
{message}
</div>
</div>
)}
</>
return props.post.type != "" ? (
<div className="tw-flex tw-justify-center">
<div
className="tw-rounded tw-max-w-xs tw-text-center tw-text-sm tw-p-1 tw-my-2 tw-opacity-70 focus:tw-opacity-100 tw-bg-light"
tabIndex={-1}
>
{message}
</div>
</div>
) : (
<div className="tw-mb-2" {...(props.isChainAdmin ? longPress : {})}>
<div
className={"tw-rounded-tl-xl tw-rounded-tr-xl tw-inline-block tw-p-2 tw-rounded-br-xl tw-ml-4".concat(
isMe ? " tw-bg-purple-shade" : " tw-bg-light",
)}
>
{username ? (
<div className="tw-text-xs tw-font-bold">{username}</div>
) : null}
<div>{message}</div>
</div>
<IonButton
onClick={() => props.onLongPress(props.post.id)}
color="transparent"
className="tw-opacity-70"
type="button"
>
<IonIcon color="dark" icon={ellipsisHorizontal} />
</IonButton>
</div>
);
}
93 changes: 68 additions & 25 deletions app/src/components/Chat/ChatWindow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,36 @@ import { IonActionSheet, IonAlert, IonIcon, useIonAlert } from "@ionic/react";
import { addOutline, build as buildFilled } from "ionicons/icons";
import { useTranslation } from "react-i18next";
import { IonAlertCustomEvent } from "@ionic/core";
import { PostList } from "@mattermost/types/posts";
import type { PostList, Post } from "@mattermost/types/posts";
import { User } from "../../api/types";
import ChatPost from "./ChatPost";
import ChatPost, { ChatPostProps } from "./ChatPost";
import { useIntersectionObserver } from "@uidotdev/usehooks";
import { useContext, useEffect, useMemo, useRef, useState } from "react";
import { useDebouncedCallback } from "use-debounce";
import { useLongPress } from "use-long-press";
import dayjs from "dayjs";
import { UserProfile } from "@mattermost/types/users";

interface Props {
channels: Channel[];
selectedChannel: Channel | null;
postList: PostList;
authUser: User;
getMmUser: (id: string) => Promise<UserProfile>;
onCreateChannel: (n: string) => void;
onSelectChannel: (c: Channel) => void;
onRenameChannel: (c: Channel, n: string) => void;
onDeleteChannel: (id: string) => void;
onDeletePost: (id: string) => void;
onScrollTop: (topPostId: string) => void;
onSendMessage: (msg: string, callback: Function) => Promise<void>;
}

// This follows the controller / view component pattern
export default function ChatWindow(props: Props) {
const { t } = useTranslation();
const { isChainAdmin, chain, chainUsers } = useContext(StoreContext);
const { authUser, isChainAdmin, chain, chainUsers } =
useContext(StoreContext);
const slowTriggerScrollTop = useDebouncedCallback(() => {
const lastPostId = props.postList.order.at(-1);
if (lastPostId) {
Expand All @@ -43,7 +48,11 @@ export default function ChatWindow(props: Props) {
root: refScrollRoot.current,
});

const [isActionSheetOpen, setIsActionSheetOpen] = useState(false);
const [isChannelActionSheetOpen, setIsChannelActionSheetOpen] =
useState(false);
const [isPostActionSheetOpen, setIsPostActionSheetOpen] = useState<
string | null
>(null);
const [presentAlert] = useIonAlert();

useEffect(() => {
Expand All @@ -56,9 +65,7 @@ export default function ChatWindow(props: Props) {
const chainChannels = useMemo(() => {
if (!chain || !chain.chat_room_ids) return [];
console.log("chainChannels", props.channels);
return props.channels
.filter((c) => chain.chat_room_ids?.includes(c.id))
.sort((a, b) => (a.create_at > b.create_at ? 1 : 0));
return props.channels.sort((a, b) => (a.create_at > b.create_at ? 1 : 0));
}, [props.channels, chain]);

function onCreateChannelSubmit(e: IonAlertCustomEvent<any>) {
Expand Down Expand Up @@ -87,16 +94,36 @@ export default function ChatWindow(props: Props) {

const longPressChannel = useLongPress(
(e) => {
setIsActionSheetOpen(true);
setIsChannelActionSheetOpen(true);
},
{
onCancel: (e) => {
setIsActionSheetOpen(false);
setIsChannelActionSheetOpen(false);
},
},
);

function handleOptionSelect(value: string) {
function handlePostOptionSelect(value: "delete") {
if (value == "delete") {
const postID = isPostActionSheetOpen;
if (!postID) return;
presentAlert({
header: "Delete post?",
buttons: [
{
text: t("cancel"),
},
{
role: "destructive",
text: t("delete"),
handler: () => props.onDeletePost(postID),
},
],
});
}
}

function handleChannelOptionSelect(value: "delete" | "rename") {
if (value == "delete") {
const handler = () => {
onDeleteChannelSubmit();
Expand Down Expand Up @@ -189,19 +216,20 @@ export default function ChatWindow(props: Props) {
})}
<IonActionSheet
header={t("chatRoomOptions")}
isOpen={isActionSheetOpen}
onDidDismiss={() => setIsActionSheetOpen(false)}
isOpen={isChannelActionSheetOpen}
onDidDismiss={() => setIsChannelActionSheetOpen(false)}
buttons={[
{
text: "Rename",
handler: () => handleOptionSelect("rename"),
text: t("rename"),
handler: () => handleChannelOptionSelect("rename"),
},
{
text: "Delete",
handler: () => handleOptionSelect("delete"),
text: t("delete"),
role: "destructive",
handler: () => handleChannelOptionSelect("delete"),
},
{
text: "Cancel",
text: t("cancel"),
role: "cancel",
},
]}
Expand Down Expand Up @@ -235,25 +263,40 @@ export default function ChatWindow(props: Props) {
ref={refScrollRoot}
className="tw-flex-grow tw-flex tw-flex-col-reverse tw-overflow-y-auto"
>
{props.postList.order.map((item, i) => {
const post = props.postList.posts[item];
const prevPostID = props.postList.order.at(i - 1);
const prevPost = prevPostID ? props.postList.posts[prevPostID] : null;
const isMe = post.user_id === props.authUser.uid;
{props.postList.order.map((postID, i) => {
const post = props.postList.posts[postID];
// const prevPostID = props.postList.order[i + 1];
// const prevPost = prevPostID ? props.postList.posts[prevPostID] : null;
return (
<ChatPost
isChainAdmin={isChainAdmin}
authUser={authUser}
onLongPress={(id) => setIsPostActionSheetOpen(id)}
post={post}
prevPost={prevPost}
getMmUser={props.getMmUser}
key={post.id}
isMe={isMe}
users={chainUsers}
/>
);
// return <span>{post.id}</span>;
})}
<span key="top" ref={refScrollTop}></span>
</div>
<ChatInput onSendMessage={onSendMessageWithCallback} />
<IonActionSheet
isOpen={isPostActionSheetOpen !== null}
onDidDismiss={() => setIsPostActionSheetOpen(null)}
buttons={[
{
text: t("delete"),
role: "destructive",
handler: () => handlePostOptionSelect("delete"),
},
{
text: t("cancel"),
role: "cancel",
},
]}
></IonActionSheet>
</div>
);
}
Loading

0 comments on commit 0716c8f

Please sign in to comment.