Skip to content

Commit

Permalink
Merge pull request #233 from litespace-org/mk/message-shape
Browse files Browse the repository at this point in the history
feat(luna): implemented new message design
  • Loading branch information
neuodev authored Dec 24, 2024
2 parents f0414f8 + 7172113 commit bcf8d6f
Show file tree
Hide file tree
Showing 10 changed files with 177 additions and 57 deletions.
12 changes: 12 additions & 0 deletions packages/assets/assets/alert-circle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions packages/assets/assets/message-cap.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions packages/assets/assets/send-2.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions packages/assets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"./Categories": "./dist/Categories.tsx",
"./LongLeftArrow": "./dist/LongLeftArrow.tsx",
"./LongRightArrow": "./dist/LongRightArrow.tsx",
"./AlertCircle": "./dist/AlertCircle.tsx",
"./AllMessages": "./dist/AllMessages.tsx",
"./ArrowDown": "./dist/ArrowDown.tsx",
"./ArrowLeft": "./dist/ArrowLeft.tsx",
Expand Down Expand Up @@ -65,6 +66,7 @@
"./Logout": "./dist/Logout.tsx",
"./Maximize": "./dist/Maximize.tsx",
"./MedalBadge": "./dist/MedalBadge.tsx",
"./MessageCap": "./dist/MessageCap.tsx",
"./MessageEdit": "./dist/MessageEdit.tsx",
"./MessageQuestion": "./dist/MessageQuestion.tsx",
"./Messages": "./dist/Messages.tsx",
Expand All @@ -91,6 +93,7 @@
"./SStar": "./dist/SStar.tsx",
"./Save": "./dist/Save.tsx",
"./Search": "./dist/Search.tsx",
"./Send2": "./dist/Send2.tsx",
"./Send": "./dist/Send.tsx",
"./SettingsScrew": "./dist/SettingsScrew.tsx",
"./Settings": "./dist/Settings.tsx",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const meta: Meta<Component> = {
decorators: [
(Story) => {
return (
<div className="tw-max-w-[352px]">
<div className="tw-max-w-[400px]">
<Story />
</div>
);
Expand Down Expand Up @@ -47,6 +47,24 @@ export const OwnerLongMessage: StoryObj<Component> = {
},
};

export const PendingMessage: StoryObj<Component> = {
args: {
message: { ...messageTemplate, text: faker.lorem.words(10) },
owner: true,
pending: true,
retry: () => alert("retry sending"),
},
};

export const ErrorMessage: StoryObj<Component> = {
args: {
message: { ...messageTemplate, text: faker.lorem.words(10) },
owner: true,
error: true,
retry: () => alert("retry sending"),
},
};

export const ReceiverShortMessage: StoryObj<Component> = {
args: {
message: { ...messageTemplate, text: faker.lorem.words(10) },
Expand Down
181 changes: 127 additions & 54 deletions packages/luna/src/components/Chat/ChatMessage/ChatMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,104 @@
import { Void } from "@litespace/types";
import React, { useState } from "react";
import React, { useMemo, useState } from "react";
import cn from "classnames";
import More from "@litespace/assets/More";
import { Menu } from "@/components/Menu";
import { useFormatMessage } from "@/hooks";
import MessageEdit from "@litespace/assets/MessageEdit";
import Trash from "@litespace/assets/Trash";
import { Typography } from "@/components/Typography";
import MessageCap from "@litespace/assets/MessageCap";
import AlertCircle from "@litespace/assets/AlertCircle";
import Send2 from "@litespace/assets/Send2";

/**
* There are now 4 states of a message:
* - Sent => (no props)
* - Pending => pending: true, error: false
* - Error => pending: false, error: true
* - Retrying => pending: true, error:true
*
* This is beside the 2 types of message:
* - Owner: The messages you send to the user
* - Reciever: The messages You get from the other user
*/
export const ChatMessage: React.FC<{
/**
* A flag that indicates whether current user is the owner of the message
*/
owner?: boolean;
/**
* A flag that indicates an error occured while sending the message
*/
error?: boolean;
/**
* A flag that removes the menu, it's used to show message in editing and deleting dialogs
*/
viewOnly?: boolean;
/**
* A flag that indicates current message is being sent to the other user
*/
pending?: boolean;
/**
* content of the message
* @param id message id
* @param text message content
*/
message: { id: number; text: string };
owner?: boolean;
/**
* resend message function
*/
retry?: Void;
/**
* edit message function, used to open the edit dialog
*/
editMessage?: Void;
/**
* delete message function, used to open the delete dialog
*/
deleteMessage?: Void;
}> = ({ message, owner, editMessage, deleteMessage, viewOnly }) => {
}> = ({
message,
owner,
editMessage,
deleteMessage,
viewOnly,
pending,
error,
retry,
}) => {
const intl = useFormatMessage();
const [showMenu, setShowMenu] = useState<boolean>(false);
const [openMenu, setOpenMenu] = useState<boolean>(false);

const menuItems = useMemo(() => {
if (error)
return [
{
label: intl("chat.message.retry"),
onClick: retry,
icon: <Send2 className="tw-w-4 tw-h-4" />,
},
];
return [
{
label: intl("chat.message.edit"),
onClick: editMessage,
icon: <MessageEdit />,
},
{
label: intl("chat.message.delete"),
onClick: deleteMessage,
icon: <Trash className="tw-w-4 tw-h-4" />,
},
];
}, [error, deleteMessage, retry, editMessage, intl]);

return (
<div
className={cn(
"tw-group tw-flex tw-w-fit",
"tw-gap-[14px] tw-items-start",
"tw-gap-[14px] tw-items-center",
{
"tw-flex-row-reverse": owner,
"tw-flex-row": !owner,
Expand All @@ -35,67 +110,65 @@ export const ChatMessage: React.FC<{
setShowMenu(false);
}}
>
{!pending && owner ? (
<div
data-show={showMenu || !!error}
className={cn(
"tw-opacity-0 data-[show=true]:tw-opacity-100 tw-transition-opacity tw-duration-200",
viewOnly && "tw-hidden"
)}
>
<Menu
actions={menuItems}
open={openMenu}
setOpen={(open: boolean) => {
if (!open) setShowMenu(false);
setOpenMenu(open);
}}
>
<div className="tw-w-6 tw-h-6 tw-flex tw-justify-center tw-items-center">
<More className="[&>*]:tw-fill-natural-800 dark:[&>*]:tw-fill-natural-50" />
</div>
</Menu>
</div>
) : null}

<div
data-show={showMenu}
className={cn(
"tw-opacity-0 data-[show=true]:tw-opacity-100 tw-transition-opacity tw-duration-200",
viewOnly && "tw-hidden"
"tw-rounded-[15px] tw-relative tw-px-6 tw-py-3",
"tw-flex tw-items-center tw-gap-2 tw-justify-start",
pending && "tw-cursor-wait",
{
"tw-bg-brand-100 dark:tw-bg-brand-100 tw-rounded-tl-none": !owner,
"tw-bg-brand-700 dark:tw-bg-brand-400 tw-rounded-tr-none": owner,
"tw-bg-destructive-700": error && !pending,
}
)}
>
<Menu
actions={[
{
label: intl("chat.message.edit"),
onClick: editMessage,
icon: <MessageEdit />,
},
{
label: intl("chat.message.delete"),
onClick: deleteMessage,
icon: <Trash />,
},
]}
open={openMenu}
setOpen={(open: boolean) => {
if (!open) setShowMenu(false);
setOpenMenu(open);
}}
>
<div className="tw-w-6 tw-h-6 tw-flex tw-justify-center tw-items-center">
<More className="[&>*]:tw-fill-natural-800 dark:[&>*]:tw-fill-natural-50" />
</div>
</Menu>
</div>
<div
className={cn("tw-rounded-[15px] tw-relative tw-px-6 tw-py-3", {
"tw-bg-brand-100 dark:tw-bg-brand-100": !owner,
"tw-bg-brand-700 dark:tw-bg-brand-400": owner,
})}
>
{!owner ? (
<div
className={cn(
"tw-absolute -tw-top-[10px] tw-left-[0px] tw-w-0 tw-h-0",
"tw-rounded-t-full tw-rounded-r-full",
"tw-border-r-[40px] tw-border-b-[22px]",
"tw-border-r-transparent tw-border-b-brand-100 dark:tw-border-b-brand-100"
)}
/>
) : (
<MessageCap
className={cn("-tw-top-1 tw-absolute", {
"-tw-right-0 [&>*]:tw-fill-brand-700": owner,
"-tw-right-0 [&>*]:tw-fill-destructive-700": error && !pending,
"-tw-left-0 tw-scale-x-[-1] [&>*]:tw-fill-brand-100": !owner,
})}
/>
{error ? (
<div
className={cn(
"tw-absolute -tw-top-[10px] tw-right-[0px] tw-w-0 tw-h-0",
"tw-rounded-t-full tw-rounded-l-full",
"tw-border-l-[40px] tw-border-b-[22px]",
"tw-border-l-transparent tw-border-b-brand-700 dark:tw-border-b-brand-400"
"tw-w-6 tw-h-6 tw-rounded-full tw-bg-destructive-500 tw-shadow-alert-circle",
"tw-flex tw-items-center tw-justify-center tw-shrink-0"
)}
/>
)}
>
<AlertCircle />
</div>
) : null}
<Typography
element="caption"
className={cn("tw-font-normal", {
"tw-text-natural-950 dark:tw-text-secondary-900": !owner,
"tw-text-natural-50 dark:tw-text-secondary-900": owner,
"tw-text-natural-50 dark:tw-text-secondary-900": owner && !pending,
"tw-text-natural-50": error && !pending,
"tw-text-natural-300": pending,
})}
>
{message.text}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const ReceiverOneMessage: StoryObj<Component> = {
name: faker.person.fullName(),
},
messages: messages(1),
owner: true,
owner: false,
sentAt: dayjs().toISOString(),
},
};
Expand Down
2 changes: 1 addition & 1 deletion packages/luna/src/components/Menu/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export const Menu: React.FC<{
key={label}
disabled={disabled}
className={cn(
"tw-flex tw-flex-row tw-gap-2 tw-p-1 tw-pe-4 tw-rounded-lg",
"tw-flex tw-flex-row tw-items-center tw-gap-2 tw-p-1 tw-pe-4 tw-rounded-lg",
"hover:tw-bg-natural-100 active:tw-bg-brand-700 tw-cursor-pointer",
"[&>span]:active:!tw-text-natural-50 [&>svg>*]:active:tw-stroke-natural-50",
"focus:tw-outline-none focus:tw-bg-natural-100",
Expand Down
1 change: 1 addition & 0 deletions packages/luna/src/locales/ar-eg.json
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@
"chat.message.delete.description": "هل انت متأكد من انك تريد حذف هذة الرسالة ؟ هذا الفعل لا يمكن الرجوع فيه.",
"chat.message.delete.confirm": "حذف",
"chat.message.delete.cancel": "الرجوع",
"chat.message.retry": "إرسال مجددا",
"chat.pin": "تثبيت",
"chat.unpin": "إلغاء تثبيت",
"chat.mute": "كتم",
Expand Down
1 change: 1 addition & 0 deletions packages/luna/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ export default {
"tutor-profile": "0px 4px 20px 0px rgba(0, 0, 0, 0.08)",
"plan-card": "0px 4px 20px 0px rgba(0, 0, 0, 0.2)",
"plan-card-label": "0px 4px 20px 0px rgba(0, 0, 0, 0.15)",
"alert-circle": "0px 4px 15px 0px rgba(0, 0, 0, 0.1)",
},
},
plugins: [
Expand Down

0 comments on commit bcf8d6f

Please sign in to comment.