Browse Source

fix: message specific styling/overflow

pull/586/head
Dan Ditomaso 1 year ago
parent
commit
300430df95
  1. 2
      src/components/PageLayout.tsx
  2. 61
      src/components/UI/Avatar.tsx
  3. 7
      src/components/UI/Sidebar/sidebarButton.tsx
  4. 3
      src/core/stores/deviceStore.ts
  5. 10
      src/pages/Messages.tsx

2
src/components/PageLayout.tsx

@ -112,7 +112,7 @@ export const PageLayout = ({
{rightBar && (
<aside
className={cn(
"w-[281px] shrink-0 border-l border-slate-300 dark:border-slate-700 p-2 overflow-hidden",
"max-w-[288px] shrink-0 border-l border-slate-300 dark:border-slate-700 p-2 overflow-hidden",
rightBarClassName
)}
>

61
src/components/UI/Avatar.tsx

@ -1,6 +1,5 @@
import { cn } from "@core/utils/cn.ts";
import { LockKeyholeOpenIcon } from 'lucide-react';
import type React from "react";
type RGBColor = {
r: number;
@ -16,7 +15,6 @@ interface AvatarProps {
showError?: boolean;
}
// biome-ignore lint/complexity/noStaticOnlyClass: stop being annoying Biome
class ColorUtils {
static hexToRgb(hex: number): RGBColor {
return {
@ -42,47 +40,43 @@ class ColorUtils {
}
}
export const Avatar: React.FC<AvatarProps> = ({
const getColorFromText = (text: string): RGBColor => {
let hash = 0;
for (let i = 0; i < text.length; i++) {
hash = text.charCodeAt(i) + ((hash << 5) - hash);
hash |= 0;
}
return {
r: (hash & 0xff0000) >> 16,
g: (hash & 0x00ff00) >> 8,
b: hash & 0x0000ff,
a: 255,
};
};
export const Avatar = ({
text,
size = "sm",
showError = false,
className,
}) => {
}: AvatarProps) => {
const sizes = {
sm: "size-10 text-xs font-light",
lg: "size-16 text-lg",
};
// Pick a color based on the text provided to function
const getColorFromText = (text: string): RGBColor => {
let hash = 0;
for (let i = 0; i < text.length; i++) {
hash = text.charCodeAt(i) + ((hash << 5) - hash);
}
return {
r: (hash & 0xff0000) >> 16,
g: (hash & 0x00ff00) >> 8,
b: hash & 0x0000ff,
a: 255,
};
};
const safeText = text?.toString().toUpperCase() ?? "UNK";
const bgColor = getColorFromText(safeText);
const isLight = ColorUtils.isLight(bgColor);
const textColor = isLight ? "#000000" : "#FFFFFF";
const initials = safeText.slice(0, 4) ?? "UNK";
const initials = safeText.slice(0, 4) || "UNK";
return (
<div
className={cn(
`flex
relative
rounded-full
items-center
justify-center
font-semibold`,
`relative flex items-center justify-center rounded-full font-semibold
`,
sizes[size],
className,
)}
@ -91,8 +85,17 @@ export const Avatar: React.FC<AvatarProps> = ({
color: textColor,
}}
>
{showError ? <LockKeyholeOpenIcon className="size-4 absolute bottom-0 right-0 z-10 text-red-500 stroke-3" /> : null}
<p className="p-1">{initials}</p>
{showError ? (
<LockKeyholeOpenIcon
className="absolute bottom-0 right-0 z-10 size-4 text-red-500 stroke-3"
aria-hidden="true"
/>
) : null}
<p
className="p-1"
>
{initials}
</p>
</div>
);
};
};

7
src/components/UI/Sidebar/sidebarButton.tsx

@ -38,6 +38,7 @@ export const SidebarButton = ({
)}
disabled={disabled}
>
{/* Icon */}
{Icon && (
<Icon
size={isCollapsed ? 20 : 18}
@ -50,9 +51,12 @@ export const SidebarButton = ({
<span
className={cn(
'flex justify-start text-left',
'min-w-0',
'text-wrap',
'px-1',
'transition-all duration-300 ease-in-out',
isCollapsed
? 'opacity-0 max-w-0 invisible flex-1 w-0 whitespace-wrap'
? 'opacity-0 max-w-0 invisible w-0 overflow-hidden'
: 'opacity-100 max-w-full visible flex-1 whitespace-normal'
)}
>
@ -63,6 +67,7 @@ export const SidebarButton = ({
<div
className={cn(
"ml-auto flex-shrink-0 justify-end text-white text-xs rounded-full px-1.5 py-0.5 bg-red-600",
"flex-shrink-0",
"transition-opacity duration-300 ease-in-out",
isCollapsed ? 'opacity-0 invisible' : 'opacity-100 visible'
)}

3
src/core/stores/deviceStore.ts

@ -41,7 +41,7 @@ export interface Device {
pendingSettingsChanges: boolean;
messageDraft: string;
unreadCounts: Map<number, number>;
nodesMap: Map<number, Protobuf.Mesh.NodeInfo>;
nodesMap: Map<number, Protobuf.Mesh.NodeInfo>; // dont access directly, use getNodes, or getNode
dialog: {
import: boolean;
QR: boolean;
@ -148,7 +148,6 @@ export const useDeviceStore = createStore<DeviceState>((set, get) => ({
unreadCounts: new Map(),
nodesMap: new Map(),
// --- Standard Setter Methods ---
setStatus: (status: Types.DeviceStatusEnum) => {
set(
produce<DeviceState>((draft) => {

10
src/pages/Messages.tsx

@ -25,7 +25,7 @@ export const MessagesPage = () => {
const { isCollapsed } = useSidebar()
const [searchTerm, setSearchTerm] = useState<string>("");
const filteredNodes: NodeInfoWithUnread[] = useMemo(() => {
const filteredNodes = (): NodeInfoWithUnread[] => {
const lowerCaseSearchTerm = searchTerm.toLowerCase();
return getNodes(node => {
@ -38,7 +38,7 @@ export const MessagesPage = () => {
unreadCount: unreadCounts.get(node.num) ?? 0,
}))
.sort((a, b) => b.unreadCount - a.unreadCount);
}, [getNodes, searchTerm, unreadCounts]);
}
const allChannels = Array.from(channels.values());
@ -119,9 +119,9 @@ export const MessagesPage = () => {
/>
</label>
<div className={cn(
"flex flex-col h-full flex-1 overflow-y-auto gap-2.5 pt-1",
"flex flex-col h-full flex-1 overflow-y-auto gap-2.5 pt-1 ",
)}>
{filteredNodes?.map((node) => (
{filteredNodes()?.map((node) => (
<SidebarButton
key={node.num}
label={node.user?.longName ?? `UNK`}
@ -179,7 +179,7 @@ export const MessagesPage = () => {
{(isBroadcast || isDirect) ? (
<MessageInput
to={isDirect ? activeChat : MessageType.Broadcast}
channel={isBroadcast ? currentChat.id : Types.ChannelNumber.Primary}
channel={isDirect ? Types.ChannelNumber.Primary : currentChat.id}
maxBytes={200}
/>
) : (

Loading…
Cancel
Save