-
-
+
+
+
{chat.type === 'group'
? chat.name
: `Chat with ${otherParticipants.length} users`}
{chat.lastMessage && (
-
+
{formatDistanceToNow(
chat.lastMessage.timestamp.toDate(),
{ addSuffix: true }
@@ -185,11 +204,11 @@ function ChatListItem({
)}
{chat.lastMessage && (
-
+
{chat.lastMessage.text}
)}
);
-}
\ No newline at end of file
+}
diff --git a/platforms/blabsy/src/components/chat/chat-window.tsx b/platforms/blabsy/src/components/chat/chat-window.tsx
index d6665d58..acbea908 100644
--- a/platforms/blabsy/src/components/chat/chat-window.tsx
+++ b/platforms/blabsy/src/components/chat/chat-window.tsx
@@ -1,14 +1,14 @@
import { useEffect, useRef, useState } from 'react';
-import { useChat } from '@lib/context/chat-context';
-import { useAuth } from '@lib/context/auth-context';
import { formatDistanceToNow } from 'date-fns';
-import type { Message } from '@lib/types/message';
import { UserIcon, PaperAirplaneIcon } from '@heroicons/react/24/outline';
import Image from 'next/image';
import { doc, getDoc } from 'firebase/firestore';
+import { useAuth } from '@lib/context/auth-context';
+import { useChat } from '@lib/context/chat-context';
import { db } from '@lib/firebase/app';
-import type { User } from '@lib/types/user';
import { Loading } from '@components/ui/loading';
+import type { Message } from '@lib/types/message';
+import type { User } from '@lib/types/user';
function MessageItem({
message,
@@ -58,27 +58,22 @@ export function ChatWindow(): JSX.Element {
const [otherUser, setOtherUser] = useState
(null);
const [isLoading, setIsLoading] = useState(false);
-
-
const otherParticipant = currentChat?.participants.find(
(p) => p !== user?.id
);
useEffect(() => {
- if (!otherParticipant) {
- return;
- }
+ if (!otherParticipant) return;
const fetchUserData = async (): Promise => {
try {
const userDoc = await getDoc(
doc(db, 'users', otherParticipant)
);
- if (userDoc.exists()) {
- setOtherUser(userDoc.data() as User);
- } else {
- }
+ if (userDoc.exists()) setOtherUser(userDoc.data() as User);
} catch (error) {
+ // eslint-disable-next-line no-console
+ console.error(error);
}
};
@@ -97,15 +92,12 @@ export function ChatWindow(): JSX.Element {
}, [currentChat]);
useEffect(() => {
- if (messagesEndRef.current) {
+ if (messagesEndRef.current)
messagesEndRef.current.scrollIntoView({ behavior: 'smooth' });
- }
}, [messages]);
useEffect(() => {
- if (!currentChat || !user) {
- return;
- }
+ if (!currentChat || !user) return;
const unreadMessages = messages?.filter(
(message) =>
@@ -113,12 +105,10 @@ export function ChatWindow(): JSX.Element {
!message.readBy.includes(user.id)
);
-
- if (unreadMessages?.length) {
+ if (unreadMessages?.length)
void Promise.all(
unreadMessages.map((message) => markAsRead(message.id))
);
- }
}, [currentChat, messages, user, markAsRead]);
const handleSubmit = async (e: React.FormEvent): Promise => {
@@ -129,6 +119,8 @@ export function ChatWindow(): JSX.Element {
await sendNewMessage(messageText);
setMessageText('');
} catch (error) {
+ // eslint-disable-next-line no-console
+ console.error('Error sending message:', error);
}
};
diff --git a/platforms/blabsy/src/components/chat/chat.tsx b/platforms/blabsy/src/components/chat/chat.tsx
index b94426d3..a44b2ce2 100644
--- a/platforms/blabsy/src/components/chat/chat.tsx
+++ b/platforms/blabsy/src/components/chat/chat.tsx
@@ -14,4 +14,4 @@ export function Chat(): JSX.Element {
);
-}
\ No newline at end of file
+}
diff --git a/platforms/blabsy/src/components/common/placeholder.tsx b/platforms/blabsy/src/components/common/placeholder.tsx
index d30a229c..41694401 100644
--- a/platforms/blabsy/src/components/common/placeholder.tsx
+++ b/platforms/blabsy/src/components/common/placeholder.tsx
@@ -2,19 +2,19 @@ import { CustomIcon } from '@components/ui/custom-icon';
import { SEO } from './seo';
export function Placeholder(): JSX.Element {
- return (
-
-
-
-
-
-
- );
+ return (
+
+
+
+
+
+
+ );
}
diff --git a/platforms/blabsy/src/components/common/seo.tsx b/platforms/blabsy/src/components/common/seo.tsx
index 91e57a19..8b5e4e13 100644
--- a/platforms/blabsy/src/components/common/seo.tsx
+++ b/platforms/blabsy/src/components/common/seo.tsx
@@ -3,29 +3,31 @@ import Head from 'next/head';
import { siteURL } from '@lib/env';
type MainLayoutProps = {
- title: string;
- image?: string;
- description?: string;
+ title: string;
+ image?: string;
+ description?: string;
};
export function SEO({
- title,
- image,
- description
+ title,
+ image,
+ description
}: MainLayoutProps): JSX.Element {
- const { asPath } = useRouter();
+ const { asPath } = useRouter();
- return (
-
-
{title}
-
- {description &&
}
- {description &&
}
- {image &&
}
-
-
- );
+ return (
+
+
{title}
+
+ {description &&
}
+ {description && (
+
+ )}
+ {image &&
}
+
+
+ );
}
diff --git a/platforms/blabsy/src/components/home/main-container.tsx b/platforms/blabsy/src/components/home/main-container.tsx
index de195d30..2923e88f 100644
--- a/platforms/blabsy/src/components/home/main-container.tsx
+++ b/platforms/blabsy/src/components/home/main-container.tsx
@@ -2,23 +2,23 @@ import cn from 'clsx';
import type { ReactNode } from 'react';
type MainContainerProps = {
- children: ReactNode;
- className?: string;
+ children: ReactNode;
+ className?: string;
};
export function MainContainer({
- children,
- className
+ children,
+ className
}: MainContainerProps): JSX.Element {
- return (
-
- {children}
-
- );
+ className
+ )}
+ >
+ {children}
+
+ );
}
diff --git a/platforms/blabsy/src/components/home/main-header.tsx b/platforms/blabsy/src/components/home/main-header.tsx
index f8f93cfa..ce9ac2b1 100644
--- a/platforms/blabsy/src/components/home/main-header.tsx
+++ b/platforms/blabsy/src/components/home/main-header.tsx
@@ -7,58 +7,58 @@ import type { ReactNode } from 'react';
import type { IconName } from '@components/ui/hero-icon';
type HomeHeaderProps = {
- tip?: string;
- title?: string;
- children?: ReactNode;
- iconName?: IconName;
- className?: string;
- disableSticky?: boolean;
- useActionButton?: boolean;
- useMobileSidebar?: boolean;
- action?: () => void;
+ tip?: string;
+ title?: string;
+ children?: ReactNode;
+ iconName?: IconName;
+ className?: string;
+ disableSticky?: boolean;
+ useActionButton?: boolean;
+ useMobileSidebar?: boolean;
+ action?: () => void;
};
export function MainHeader({
- tip,
- title,
- children,
- iconName,
- className,
- disableSticky,
- useActionButton,
- useMobileSidebar,
- action
+ tip,
+ title,
+ children,
+ iconName,
+ className,
+ disableSticky,
+ useActionButton,
+ useMobileSidebar,
+ action
}: HomeHeaderProps): JSX.Element {
- return (
-
- );
+ {useActionButton && (
+
+ )}
+ {title && (
+
+ {useMobileSidebar && }
+
+ {title}
+
+
+ )}
+ {children}
+
+ );
}
diff --git a/platforms/blabsy/src/components/input/image-preview.tsx b/platforms/blabsy/src/components/input/image-preview.tsx
index 3d204e9b..a6f55fe3 100644
--- a/platforms/blabsy/src/components/input/image-preview.tsx
+++ b/platforms/blabsy/src/components/input/image-preview.tsx
@@ -13,186 +13,200 @@ import type { MotionProps } from 'framer-motion';
import type { ImagesPreview, ImageData } from '@lib/types/file';
type ImagePreviewProps = {
- tweet?: boolean;
- viewTweet?: boolean;
- previewCount: number;
- imagesPreview: ImagesPreview;
- removeImage?: (targetId: string) => () => void;
+ tweet?: boolean;
+ viewTweet?: boolean;
+ previewCount: number;
+ imagesPreview: ImagesPreview;
+ removeImage?: (targetId: string) => () => void;
};
const variants: MotionProps = {
- initial: { opacity: 0, scale: 0.5 },
- animate: {
- opacity: 1,
- scale: 1,
- transition: { duration: 0.3 }
- },
- exit: { opacity: 0, scale: 0.5 },
- transition: { type: 'spring', duration: 0.5 }
+ initial: { opacity: 0, scale: 0.5 },
+ animate: {
+ opacity: 1,
+ scale: 1,
+ transition: { duration: 0.3 }
+ },
+ exit: { opacity: 0, scale: 0.5 },
+ transition: { type: 'spring', duration: 0.5 }
};
type PostImageBorderRadius = Record
;
const postImageBorderRadius: Readonly = {
- 1: ['rounded-2xl'],
- 2: ['rounded-tl-2xl rounded-bl-2xl', 'rounded-tr-2xl rounded-br-2xl'],
- 3: ['rounded-tl-2xl rounded-bl-2xl', 'rounded-tr-2xl', 'rounded-br-2xl'],
- 4: ['rounded-tl-2xl', 'rounded-tr-2xl', 'rounded-bl-2xl', 'rounded-br-2xl']
+ 1: ['rounded-2xl'],
+ 2: ['rounded-tl-2xl rounded-bl-2xl', 'rounded-tr-2xl rounded-br-2xl'],
+ 3: ['rounded-tl-2xl rounded-bl-2xl', 'rounded-tr-2xl', 'rounded-br-2xl'],
+ 4: ['rounded-tl-2xl', 'rounded-tr-2xl', 'rounded-bl-2xl', 'rounded-br-2xl']
};
export function ImagePreview({
- tweet,
- viewTweet,
- previewCount,
- imagesPreview,
- removeImage
+ tweet,
+ viewTweet,
+ previewCount,
+ imagesPreview,
+ removeImage
}: ImagePreviewProps): JSX.Element {
- const [selectedIndex, setSelectedIndex] = useState(0);
- const [selectedImage, setSelectedImage] = useState(null);
-
- const videoRef = useRef(null);
-
- const { open, openModal, closeModal } = useModal();
-
- useEffect(() => {
- const imageData = imagesPreview[selectedIndex];
- setSelectedImage(imageData);
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [selectedIndex]);
-
- const handleVideoStop = (): void => {
- if (videoRef.current) videoRef.current.pause();
- };
-
- const handleSelectedImage = (index: number, isVideo?: boolean) => () => {
- if (isVideo) handleVideoStop();
-
- setSelectedIndex(index);
- openModal();
- };
-
- const handleNextIndex = (type: 'prev' | 'next') => () => {
- const nextIndex =
- type === 'prev'
- ? selectedIndex === 0
- ? previewCount - 1
- : selectedIndex - 1
- : selectedIndex === previewCount - 1
- ? 0
- : selectedIndex + 1;
-
- setSelectedIndex(nextIndex);
- };
-
- const isTweet = tweet ?? viewTweet;
-
- return (
-
-
-
-
-
- {imagesPreview.map(({ id, src, alt }, index) => {
- const isVideo = imagesPreview[index].type?.includes('video');
-
- return (
- (null);
+
+ const videoRef = useRef(null);
+
+ const { open, openModal, closeModal } = useModal();
+
+ useEffect(() => {
+ const imageData = imagesPreview[selectedIndex];
+ setSelectedImage(imageData);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [selectedIndex]);
+
+ const handleVideoStop = (): void => {
+ if (videoRef.current) videoRef.current.pause();
+ };
+
+ const handleSelectedImage = (index: number, isVideo?: boolean) => () => {
+ if (isVideo) handleVideoStop();
+
+ setSelectedIndex(index);
+ openModal();
+ };
+
+ const handleNextIndex = (type: 'prev' | 'next') => () => {
+ const nextIndex =
+ type === 'prev'
+ ? selectedIndex === 0
+ ? previewCount - 1
+ : selectedIndex - 1
+ : selectedIndex === previewCount - 1
+ ? 0
+ : selectedIndex + 1;
+
+ setSelectedIndex(nextIndex);
+ };
+
+ const isTweet = tweet ?? viewTweet;
+
+ return (
+
+
- {isVideo ? (
- <>
-
+
- >
- ) : (
-
+ >
+ ) : (
+
- )}
- {removeImage && (
-
+ )}
+ {removeImage && (
+
-
-
-
- )}
-
- );
- })}
-
-
- );
+ onClick={preventBubbling(removeImage(id))}
+ >
+
+
+
+ )}
+
+ );
+ })}
+
+
+ );
}
diff --git a/platforms/blabsy/src/components/input/input-accent-radio.tsx b/platforms/blabsy/src/components/input/input-accent-radio.tsx
index 1239c0fc..17c3f4c4 100644
--- a/platforms/blabsy/src/components/input/input-accent-radio.tsx
+++ b/platforms/blabsy/src/components/input/input-accent-radio.tsx
@@ -4,54 +4,50 @@ import { HeroIcon } from '@components/ui/hero-icon';
import type { Accent } from '@lib/types/theme';
type InputAccentRadioProps = {
- type: Accent;
+ type: Accent;
};
type InputAccentData = Record;
const InputColors: Readonly = {
- yellow:
- 'bg-accent-yellow hover:ring-accent-yellow/10 active:ring-accent-yellow/20',
- blue: 'bg-accent-blue hover:ring-accent-blue/10 active:ring-accent-blue/20',
- pink: 'bg-accent-pink hover:ring-accent-pink/10 active:ring-accent-pink/20',
- purple:
- 'bg-accent-purple hover:ring-accent-purple/10 active:ring-accent-purple/20',
- orange:
- 'bg-accent-orange hover:ring-accent-orange/10 active:ring-accent-orange/20',
- green:
- 'bg-accent-green hover:ring-accent-green/10 active:ring-accent-green/20'
+ yellow: 'bg-accent-yellow hover:ring-accent-yellow/10 active:ring-accent-yellow/20',
+ blue: 'bg-accent-blue hover:ring-accent-blue/10 active:ring-accent-blue/20',
+ pink: 'bg-accent-pink hover:ring-accent-pink/10 active:ring-accent-pink/20',
+ purple: 'bg-accent-purple hover:ring-accent-purple/10 active:ring-accent-purple/20',
+ orange: 'bg-accent-orange hover:ring-accent-orange/10 active:ring-accent-orange/20',
+ green: 'bg-accent-green hover:ring-accent-green/10 active:ring-accent-green/20'
};
export function InputAccentRadio({ type }: InputAccentRadioProps): JSX.Element {
- const { accent, changeAccent } = useTheme();
+ const { accent, changeAccent } = useTheme();
- const bgColor = InputColors[type];
- const isChecked = type === accent;
+ const bgColor = InputColors[type];
+ const isChecked = type === accent;
- return (
-
- );
+ bgColor
+ )}
+ htmlFor={type}
+ >
+
+
+
+
+
+ );
}
diff --git a/platforms/blabsy/src/components/input/input-field.tsx b/platforms/blabsy/src/components/input/input-field.tsx
index 4627fe32..d218b8f4 100644
--- a/platforms/blabsy/src/components/input/input-field.tsx
+++ b/platforms/blabsy/src/components/input/input-field.tsx
@@ -3,99 +3,105 @@ import type { User, EditableData } from '@lib/types/user';
import type { KeyboardEvent, ChangeEvent } from 'react';
export type InputFieldProps = {
- label: string;
- inputId: EditableData | Extract;
- inputValue: string | null;
- inputLimit?: number;
- useTextArea?: boolean;
- errorMessage?: string;
- handleChange: (
- e: ChangeEvent
- ) => void;
- handleKeyboardShortcut?: ({
- key,
- ctrlKey
- }: KeyboardEvent) => void;
+ label: string;
+ inputId: EditableData | Extract;
+ inputValue: string | null;
+ inputLimit?: number;
+ useTextArea?: boolean;
+ errorMessage?: string;
+ handleChange: (
+ e: ChangeEvent
+ ) => void;
+ handleKeyboardShortcut?: ({
+ key,
+ ctrlKey
+ }: KeyboardEvent) => void;
};
export function InputField({
- label,
- inputId,
- inputValue,
- inputLimit,
- useTextArea,
- errorMessage,
- handleChange,
- handleKeyboardShortcut
+ label,
+ inputId,
+ inputValue,
+ inputLimit,
+ useTextArea,
+ errorMessage,
+ handleChange,
+ handleKeyboardShortcut
}: InputFieldProps): JSX.Element {
- const slicedInputValue = inputValue?.slice(0, inputLimit) ?? '';
+ const slicedInputValue = inputValue?.slice(0, inputLimit) ?? '';
- const inputLength = slicedInputValue.length;
- const isHittingInputLimit = inputLimit && inputLength > inputLimit;
+ const inputLength = slicedInputValue.length;
+ const isHittingInputLimit = inputLimit && inputLength > inputLimit;
- return (
-
-
+
- {useTextArea ? (
-
- ) : (
-
- )}
-
+ )}
+
- {inputLimit && (
-
+ {label}
+
+ {inputLimit && (
+
+ {inputLength} / {inputLimit}
+
+ )}
+
+ {errorMessage && (
+
{errorMessage}
)}
- >
- {inputLength} / {inputLimit}
-
- )}
-
- {errorMessage && (
-
{errorMessage}
- )}
-
- );
+
+ );
}
diff --git a/platforms/blabsy/src/components/input/input-theme-radio.tsx b/platforms/blabsy/src/components/input/input-theme-radio.tsx
index 8fa53dfd..87c1424c 100644
--- a/platforms/blabsy/src/components/input/input-theme-radio.tsx
+++ b/platforms/blabsy/src/components/input/input-theme-radio.tsx
@@ -4,92 +4,96 @@ import { HeroIcon } from '@components/ui/hero-icon';
import type { Theme } from '@lib/types/theme';
type InputThemeRadioProps = {
- type: Theme;
- label: string;
+ type: Theme;
+ label: string;
};
type InputThemeData = Record<
- Theme,
- {
- textColor: string;
- backgroundColor: string;
- iconBorderColor: string;
- hoverBackgroundColor: string;
- }
+ Theme,
+ {
+ textColor: string;
+ backgroundColor: string;
+ iconBorderColor: string;
+ hoverBackgroundColor: string;
+ }
>;
const inputThemeData: Readonly