11import { BookmarkIcon , EyeOffIcon , MessageCircleMoreIcon } from "lucide-react" ;
22import { observer } from "mobx-react-lite" ;
3- import { memo , useCallback , useState } from "react" ;
3+ import { memo , useCallback , useEffect , useRef , useState } from "react" ;
4+ import toast from "react-hot-toast" ;
45import { Link , useLocation } from "react-router-dom" ;
56import { Tooltip , TooltipContent , TooltipProvider , TooltipTrigger } from "@/components/ui/tooltip" ;
67import useAsyncEffect from "@/hooks/useAsyncEffect" ;
@@ -50,6 +51,8 @@ const MemoView: React.FC<Props> = observer((props: Props) => {
5051 urls : [ ] ,
5152 index : 0 ,
5253 } ) ;
54+ const [ shortcutActive , setShortcutActive ] = useState ( false ) ;
55+ const cardRef = useRef < HTMLDivElement > ( null ) ;
5356 const instanceMemoRelatedSetting = instanceStore . state . memoRelatedSetting ;
5457 const referencedMemos = memo . relations . filter ( ( relation ) => relation . type === MemoRelation_Type . REFERENCE ) ;
5558 const commentAmount = memo . relations . filter (
@@ -124,6 +127,89 @@ const MemoView: React.FC<Props> = observer((props: Props) => {
124127 }
125128 } ;
126129
130+ const archiveMemo = useCallback ( async ( ) => {
131+ if ( isArchived ) {
132+ return ;
133+ }
134+
135+ try {
136+ await memoStore . updateMemo (
137+ {
138+ name : memo . name ,
139+ state : State . ARCHIVED ,
140+ } ,
141+ [ "state" ] ,
142+ ) ;
143+ toast . success ( t ( "message.archived-successfully" ) ) ;
144+ userStore . setStatsStateId ( ) ;
145+ } catch ( error : any ) {
146+ console . error ( error ) ;
147+ toast . error ( error ?. details ) ;
148+ }
149+ } , [ isArchived , memo . name , t , memoStore , userStore ] ) ;
150+
151+ useEffect ( ( ) => {
152+ if ( ! shortcutActive || readonly || showEditor || ! cardRef . current ) {
153+ return ;
154+ }
155+
156+ const cardEl = cardRef . current ;
157+ const isTextInputElement = ( element : HTMLElement | null ) => {
158+ if ( ! element ) {
159+ return false ;
160+ }
161+ if ( element . isContentEditable ) {
162+ return true ;
163+ }
164+ if ( element instanceof HTMLTextAreaElement ) {
165+ return true ;
166+ }
167+
168+ if ( element instanceof HTMLInputElement ) {
169+ const textTypes = [ "text" , "search" , "email" , "password" , "url" , "tel" , "number" ] ;
170+ return textTypes . includes ( element . type || "text" ) ;
171+ }
172+
173+ return false ;
174+ } ;
175+
176+ const handleKeyDown = ( event : KeyboardEvent ) => {
177+ const target = event . target as HTMLElement | null ;
178+ if ( ! cardEl . contains ( target ) || isTextInputElement ( target ) ) {
179+ return ;
180+ }
181+
182+ if ( event . metaKey || event . ctrlKey || event . altKey ) {
183+ return ;
184+ }
185+
186+ const key = event . key . toLowerCase ( ) ;
187+ if ( key === "e" ) {
188+ event . preventDefault ( ) ;
189+ setShowEditor ( true ) ;
190+ } else if ( key === "a" && ! isArchived ) {
191+ event . preventDefault ( ) ;
192+ archiveMemo ( ) ;
193+ }
194+ } ;
195+
196+ cardEl . addEventListener ( "keydown" , handleKeyDown ) ;
197+ return ( ) => cardEl . removeEventListener ( "keydown" , handleKeyDown ) ;
198+ } , [ shortcutActive , readonly , showEditor , isArchived , archiveMemo ] ) ;
199+
200+ useEffect ( ( ) => {
201+ if ( showEditor || readonly ) {
202+ setShortcutActive ( false ) ;
203+ }
204+ } , [ showEditor , readonly ] ) ;
205+
206+ const handleShortcutActivation = ( active : boolean ) => {
207+ if ( readonly ) {
208+ return ;
209+ }
210+ setShortcutActive ( active ) ;
211+ } ;
212+
127213 const displayTime = isArchived ? (
128214 memo . displayTime ?. toLocaleString ( )
129215 ) : (
@@ -142,9 +228,14 @@ const MemoView: React.FC<Props> = observer((props: Props) => {
142228 ) : (
143229 < div
144230 className = { cn (
145- "relative group flex flex-col justify-start items-start bg-card w-full px-4 py-3 mb-2 gap-2 text-card-foreground rounded-lg border border-border transition-colors" ,
231+ "relative group flex flex-col justify-start items-start bg-card w-full px-4 py-3 mb-2 gap-2 text-card-foreground rounded-lg border border-border transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" ,
232+ shortcutActive && ! showEditor && "border-ring ring-2 ring-ring bg-accent/10" ,
146233 className ,
147234 ) }
235+ ref = { cardRef }
236+ tabIndex = { readonly ? - 1 : 0 }
237+ onFocus = { ( ) => handleShortcutActivation ( true ) }
238+ onBlur = { ( ) => handleShortcutActivation ( false ) }
148239 >
149240 < div className = "w-full flex flex-row justify-between items-center gap-2" >
150241 < div className = "w-auto max-w-[calc(100%-8rem)] grow flex flex-row justify-start items-center" >
0 commit comments