Add keyboard mode toggle and QR scanner improvements

- Add keyboard mode toggle button (⇧K) in sidebar
- Triple-Escape to quickly exit keyboard mode
- Extract QrScannerModal to shared component
- Add QR scanner for NWC wallet connection in Settings
- Update Help page with keyboard toggle documentation
- Fix keyboard navigation getting stuck on inbox
- Improve feed loading after login (loads immediately)
- DM conversation page layout improvements

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
woikos
2026-01-04 11:08:06 +01:00
parent 4c3e8d5cc7
commit d1ec24b85a
16 changed files with 1224 additions and 476 deletions

View File

@@ -77,11 +77,11 @@ export default function RepostButton({ stuff }: { stuff: Event | string }) {
const trigger = (
<button
className={cn(
'flex gap-1 items-center px-3 h-full enabled:hover:text-lime-500 disabled:text-muted-foreground/40',
'flex gap-1 items-center px-3 h-full enabled:hover:text-lime-500 disabled:text-muted-foreground/40 group',
hasReposted ? 'text-lime-500' : 'text-muted-foreground'
)}
disabled={!event}
title={t('Repost')}
title={t('Repost (p) / Quote (q)')}
data-action="repost"
onClick={() => {
if (!event) return
@@ -91,7 +91,10 @@ export default function RepostButton({ stuff }: { stuff: Event | string }) {
}
}}
>
{reposting ? <Loader className="animate-spin" /> : <Repeat />}
<span className="relative">
{reposting ? <Loader className="animate-spin" /> : <Repeat />}
<kbd className="absolute -top-1.5 -right-2 text-[9px] font-mono opacity-50 group-hover:opacity-100">p</kbd>
</span>
{!!repostCount && <div className="text-sm">{formatCount(repostCount)}</div>}
</button>
)
@@ -108,10 +111,25 @@ export default function RepostButton({ stuff }: { stuff: Event | string }) {
/>
)
// Hidden button for keyboard shortcut (q for quote)
const quoteButton = (
<button
className="hidden"
data-action="quote"
onClick={(e) => {
e.stopPropagation()
checkLogin(() => {
setIsPostDialogOpen(true)
})
}}
/>
)
if (isSmallScreen) {
return (
<>
{trigger}
{quoteButton}
<Drawer open={isDrawerOpen} onOpenChange={setIsDrawerOpen}>
<DrawerOverlay onClick={() => setIsDrawerOpen(false)} />
<DrawerContent hideOverlay>
@@ -170,12 +188,12 @@ export default function RepostButton({ stuff }: { stuff: Event | string }) {
setIsPostDialogOpen(true)
})
}}
data-action="quote"
>
<PencilLine /> {t('Quote')}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
{quoteButton}
{postEditor}
</>
)