mirror of
https://github.com/Mail-0/Zero.git
synced 2026-06-28 23:06:54 +00:00
fixing small bugs (#1204)
# WIP ## Icon Styling and Keyboard Shortcut Improvements ### TL;DR Improved icon styling and keyboard shortcuts for better user experience in the mail application. ### What changed? - Fixed icon styling by changing `--icon-color` from `black` to `currentColor` for better theme compatibility - Enhanced the ExclamationCircle icon appearance with conditional styling based on importance status - Adjusted Star icon size for better visual consistency - Moved mouse event handlers from mail-list to mail layout component for proper scope management - Added Cmd+Z (Ctrl+Z) keyboard shortcut for undoing the last action - Fixed OldPhone icon SVG formatting - Implemented the undoLastAction handler in global hotkeys ### How to test? 1. Verify icon appearance in both light and dark themes 2. Check that the ExclamationCircle icon properly changes appearance when marking emails as important 3. Test the new Cmd+Z (Ctrl+Z) keyboard shortcut to undo actions 4. Confirm that keyboard shortcuts work correctly when hovering over the mail list ### Why make this change? These changes improve the visual consistency of icons across themes and enhance the user experience by adding expected keyboard shortcuts like undo functionality. The refactoring of mouse event handlers ensures proper keyboard shortcut scope management when interacting with the mail list. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added global keyboard shortcut (Cmd/Ctrl + Z) to undo the last action in the mail app. - **Enhancements** - Improved icon appearance with dynamic color inheritance and updated important/flagged icon styling. - Adjusted icon sizing for better consistency. - Hotkey scope for the mail list is now enabled or disabled based on mouse hover over the mail list panel. - **Style** - Minor formatting improvements to icon components. - **Bug Fixes** - Ensured undo shortcut prevents default browser behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -48,7 +48,7 @@
|
||||
--sidebar-accent-foreground: 240 5.9% 10%;
|
||||
--sidebar-border: 220 13% 91%;
|
||||
--sidebar-ring: 217.2 91.2% 59.8%;
|
||||
--icon-color: black;
|
||||
--icon-color: currentColor;
|
||||
}
|
||||
|
||||
.dark {
|
||||
|
||||
@@ -425,8 +425,8 @@ export const ExclamationCircle = ({ className }: { className?: string }) => (
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M7 13.5C10.5899 13.5 13.5 10.5899 13.5 7C13.5 3.41015 10.5899 0.5 7 0.5C3.41015 0.5 0.5 3.41015 0.5 7C0.5 10.5899 3.41015 13.5 7 13.5ZM7 3.28571C7.38463 3.28571 7.69643 3.59752 7.69643 3.98214V6.76786C7.69643 7.15248 7.38463 7.46429 7 7.46429C6.61537 7.46429 6.30357 7.15248 6.30357 6.76786V3.98214C6.30357 3.59752 6.61537 3.28571 7 3.28571ZM7 10.7143C7.51284 10.7143 7.92857 10.2986 7.92857 9.78571C7.92857 9.27288 7.51284 8.85714 7 8.85714C6.48716 8.85714 6.07143 9.27288 6.07143 9.78571C6.07143 10.2986 6.48716 10.7143 7 10.7143Z"
|
||||
fill="var(--icon-color)"
|
||||
fillOpacity="0.5"
|
||||
// fill="var(--icon-color)"
|
||||
// fillOpacity="0.5"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -1763,12 +1763,18 @@ export const Printer = ({ className }: { className?: string }) => (
|
||||
);
|
||||
|
||||
export const OldPhone = ({ className }: { className?: string }) => (
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg" className={className}>
|
||||
<svg
|
||||
width="22"
|
||||
height="22"
|
||||
viewBox="0 0 22 22"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M0.5 3.5C0.5 1.84315 1.84315 0.5 3.5 0.5H4.87163C5.732 0.5 6.48197 1.08556 6.69064 1.92025L7.79644 6.34343C7.97941 7.0753 7.70594 7.84555 7.10242 8.29818L5.8088 9.2684C5.67447 9.36915 5.64527 9.51668 5.683 9.61969C6.81851 12.7195 9.28051 15.1815 12.3803 16.317C12.4833 16.3547 12.6309 16.3255 12.7316 16.1912L13.7018 14.8976C14.1545 14.2941 14.9247 14.0206 15.6566 14.2036L20.0798 15.3094C20.9144 15.518 21.5 16.268 21.5 17.1284V18.5C21.5 20.1569 20.1569 21.5 18.5 21.5H16.25C7.55151 21.5 0.5 14.4485 0.5 5.75V3.5Z"
|
||||
/>
|
||||
|
||||
</svg>
|
||||
);
|
||||
|
||||
@@ -271,7 +271,13 @@ const Thread = memo(
|
||||
className="h-6 w-6 [&_svg]:size-3.5"
|
||||
onClick={handleToggleImportant}
|
||||
>
|
||||
<ExclamationCircle className={cn(displayImportant ? '' : 'opacity-25')} />
|
||||
<ExclamationCircle
|
||||
className={cn(
|
||||
displayImportant
|
||||
? 'fill-orange-400 stroke-orange-400'
|
||||
: 'fill-transparent stroke-[#9D9D9D] dark:stroke-[#9D9D9D]',
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent
|
||||
@@ -808,12 +814,6 @@ export const MailList = memo(
|
||||
'hide-link-indicator flex h-full w-full',
|
||||
getSelectMode() === 'range' && 'select-none',
|
||||
)}
|
||||
onMouseEnter={() => {
|
||||
enableScope('mail-list');
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
disableScope('mail-list');
|
||||
}}
|
||||
>
|
||||
<>
|
||||
{isLoading ? (
|
||||
@@ -945,7 +945,7 @@ function getLabelIcon(label: string) {
|
||||
|
||||
switch (normalizedLabel) {
|
||||
case 'starred':
|
||||
return <Star className="h-3.5 w-3.5 fill-yellow-400 stroke-yellow-400" />;
|
||||
return <Star className="h-[12px] w-[12px] fill-yellow-400 stroke-yellow-400" />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -432,6 +432,15 @@ export function MailLayout() {
|
||||
disableScope('mail-list');
|
||||
};
|
||||
}, [threadId, enableScope, disableScope]);
|
||||
|
||||
const handleMailListMouseEnter = useCallback(() => {
|
||||
enableScope('mail-list');
|
||||
}, [enableScope]);
|
||||
|
||||
const handleMailListMouseLeave = useCallback(() => {
|
||||
disableScope('mail-list');
|
||||
}, [disableScope]);
|
||||
|
||||
const [, setActiveReplyId] = useQueryState('activeReplyId');
|
||||
|
||||
// Add mailto protocol handler registration
|
||||
@@ -472,6 +481,8 @@ export function MailLayout() {
|
||||
`bg-panelLight dark:bg-panelDark mb-1 w-fit shadow-sm md:rounded-2xl md:border md:border-[#E7E7E7] lg:flex lg:shadow-sm dark:border-[#252525]`,
|
||||
isDesktop && threadId && 'hidden lg:block',
|
||||
)}
|
||||
onMouseEnter={handleMailListMouseEnter}
|
||||
onMouseLeave={handleMailListMouseLeave}
|
||||
>
|
||||
<div className="w-full md:h-[calc(100dvh-10px)]">
|
||||
<div
|
||||
|
||||
@@ -118,13 +118,14 @@ const globalShortcuts: Shortcut[] = [
|
||||
// description: 'Show keyboard shortcuts',
|
||||
// scope: 'global',
|
||||
// },
|
||||
// {
|
||||
// keys: ['z'],
|
||||
// action: 'undoLastAction',
|
||||
// type: 'single',
|
||||
// description: 'Undo last action',
|
||||
// scope: 'global',
|
||||
// },
|
||||
{
|
||||
keys: ['mod', 'z'],
|
||||
action: 'undoLastAction',
|
||||
type: 'single',
|
||||
description: 'Undo last action',
|
||||
scope: 'global',
|
||||
preventDefault: true,
|
||||
},
|
||||
{
|
||||
keys: ['v'],
|
||||
action: 'openVoice',
|
||||
|
||||
@@ -19,6 +19,7 @@ type PendingAction = {
|
||||
optimisticId: string;
|
||||
execute: () => Promise<void>;
|
||||
undo: () => void;
|
||||
toastId?: string | number;
|
||||
};
|
||||
|
||||
export function useOptimisticActions() {
|
||||
@@ -41,6 +42,7 @@ export function useOptimisticActions() {
|
||||
|
||||
const pendingActionsRef = useRef<Map<string, PendingAction>>(new Map());
|
||||
const pendingActionsByTypeRef = useRef<Map<string, Set<string>>>(new Map());
|
||||
const lastActionIdRef = useRef<string | null>(null);
|
||||
|
||||
const generatePendingActionId = () =>
|
||||
`pending_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
||||
@@ -406,6 +408,24 @@ export function useOptimisticActions() {
|
||||
],
|
||||
);
|
||||
|
||||
const undoLastAction = useCallback(() => {
|
||||
if (!lastActionIdRef.current) return;
|
||||
|
||||
const lastAction = pendingActionsRef.current.get(lastActionIdRef.current);
|
||||
if (!lastAction) return;
|
||||
|
||||
lastAction.undo();
|
||||
|
||||
pendingActionsRef.current.delete(lastActionIdRef.current);
|
||||
pendingActionsByTypeRef.current.get(lastAction.type)?.delete(lastActionIdRef.current);
|
||||
|
||||
if (lastAction.toastId) {
|
||||
toast.dismiss(lastAction.toastId);
|
||||
}
|
||||
|
||||
lastActionIdRef.current = null;
|
||||
}, []);
|
||||
|
||||
return {
|
||||
optimisticMarkAsRead,
|
||||
optimisticMarkAsUnread,
|
||||
@@ -413,5 +433,6 @@ export function useOptimisticActions() {
|
||||
optimisticMoveThreadsTo,
|
||||
optimisticDeleteThreads,
|
||||
optimisticToggleImportant,
|
||||
undoLastAction,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useCommandPalette } from '@/components/context/command-palette-context';
|
||||
import { useOptimisticActions } from '@/hooks/use-optimistic-actions';
|
||||
import { keyboardShortcuts } from '@/config/shortcuts';
|
||||
import { useVoice } from '@/providers/voice-provider';
|
||||
import { useShortcuts } from './use-hotkey-utils';
|
||||
@@ -7,6 +8,7 @@ import { useQueryState } from 'nuqs';
|
||||
export function GlobalHotkeys() {
|
||||
const [composeOpen, setComposeOpen] = useQueryState('isComposeOpen');
|
||||
const { openModal, clearAllFilters } = useCommandPalette();
|
||||
const { undoLastAction } = useOptimisticActions();
|
||||
const { startConversation } = useVoice();
|
||||
const scope = 'global';
|
||||
|
||||
@@ -17,6 +19,9 @@ export function GlobalHotkeys() {
|
||||
newEmail: () => setComposeOpen('true'),
|
||||
commandPalette: () => openModal(),
|
||||
clearAllFilters: () => clearAllFilters(),
|
||||
undoLastAction: () => {
|
||||
undoLastAction();
|
||||
},
|
||||
};
|
||||
|
||||
const globalShortcuts = keyboardShortcuts.filter((shortcut) => shortcut.scope === scope);
|
||||
|
||||
Reference in New Issue
Block a user