mirror of
https://github.com/ManInDark/HabitTrove.git
synced 2026-01-21 06:34:30 +01:00
fix emoji picker and about modal (#146)
This commit is contained in:
@@ -1,5 +1,12 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Version 0.2.17
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* fix emoji selector (#142)
|
||||||
|
* fix about modal (#145)
|
||||||
|
|
||||||
## Version 0.2.16
|
## Version 0.2.16
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|||||||
@@ -11,17 +11,16 @@ import ChangelogModal from "./ChangelogModal"
|
|||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
|
|
||||||
interface AboutModalProps {
|
interface AboutModalProps {
|
||||||
isOpen: boolean
|
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AboutModal({ isOpen, onClose }: AboutModalProps) {
|
export default function AboutModal({ onClose }: AboutModalProps) {
|
||||||
const t = useTranslations('AboutModal')
|
const t = useTranslations('AboutModal')
|
||||||
const version = packageJson.version
|
const version = packageJson.version
|
||||||
const [changelogOpen, setChangelogOpen] = useState(false)
|
const [changelogOpen, setChangelogOpen] = useState(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={isOpen} onOpenChange={onClose}>
|
<Dialog open={true} onOpenChange={onClose}>
|
||||||
<DialogContent className="max-w-sm">
|
<DialogContent className="max-w-sm">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle aria-label={t('dialogArisLabel')}></DialogTitle>
|
<DialogTitle aria-label={t('dialogArisLabel')}></DialogTitle>
|
||||||
|
|||||||
@@ -8,26 +8,16 @@ import { settingsAtom, browserSettingsAtom, usersAtom } from '@/lib/atoms'
|
|||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar'
|
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar'
|
||||||
import { Switch } from '@/components/ui/switch'
|
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
import { Textarea } from '@/components/ui/textarea'
|
import { Textarea } from '@/components/ui/textarea'
|
||||||
import { Info, SmilePlus, Zap } from 'lucide-react'
|
import { Zap } from 'lucide-react'
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||||
import data from '@emoji-mart/data'
|
|
||||||
import Picker from '@emoji-mart/react'
|
|
||||||
import { Habit, SafeUser } from '@/lib/types'
|
import { Habit, SafeUser } from '@/lib/types'
|
||||||
|
import EmojiPickerButton from './EmojiPickerButton'
|
||||||
import { convertHumanReadableFrequencyToMachineReadable, convertMachineReadableFrequencyToHumanReadable, d2s, d2t, serializeRRule } from '@/lib/utils'
|
import { convertHumanReadableFrequencyToMachineReadable, convertMachineReadableFrequencyToHumanReadable, d2s, d2t, serializeRRule } from '@/lib/utils'
|
||||||
import { INITIAL_DUE, INITIAL_RECURRENCE_RULE, QUICK_DATES, RECURRENCE_RULE_MAP, MAX_COIN_LIMIT } from '@/lib/constants'
|
import { INITIAL_DUE, INITIAL_RECURRENCE_RULE, QUICK_DATES, RECURRENCE_RULE_MAP, MAX_COIN_LIMIT } from '@/lib/constants'
|
||||||
import * as chrono from 'chrono-node';
|
|
||||||
import { DateTime } from 'luxon'
|
import { DateTime } from 'luxon'
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from "@/components/ui/select"
|
|
||||||
import { useHelpers } from '@/lib/client-helpers'
|
import { useHelpers } from '@/lib/client-helpers'
|
||||||
|
|
||||||
interface AddEditHabitModalProps {
|
interface AddEditHabitModalProps {
|
||||||
@@ -46,9 +36,9 @@ export default function AddEditHabitModal({ onClose, onSave, habit, isTask }: Ad
|
|||||||
const [targetCompletions, setTargetCompletions] = useState(habit?.targetCompletions || 1)
|
const [targetCompletions, setTargetCompletions] = useState(habit?.targetCompletions || 1)
|
||||||
const isRecurRule = !isTask
|
const isRecurRule = !isTask
|
||||||
// Initialize ruleText with the actual frequency string or default, not the display text
|
// Initialize ruleText with the actual frequency string or default, not the display text
|
||||||
const initialRuleText = habit?.frequency ? convertMachineReadableFrequencyToHumanReadable({
|
const initialRuleText = habit?.frequency ? convertMachineReadableFrequencyToHumanReadable({
|
||||||
frequency: habit.frequency,
|
frequency: habit.frequency,
|
||||||
isRecurRule,
|
isRecurRule,
|
||||||
timezone: settings.system.timezone
|
timezone: settings.system.timezone
|
||||||
}) : (isRecurRule ? INITIAL_RECURRENCE_RULE : INITIAL_DUE);
|
}) : (isRecurRule ? INITIAL_RECURRENCE_RULE : INITIAL_DUE);
|
||||||
const [ruleText, setRuleText] = useState<string>(initialRuleText)
|
const [ruleText, setRuleText] = useState<string>(initialRuleText)
|
||||||
@@ -119,33 +109,15 @@ export default function AddEditHabitModal({ onClose, onSave, habit, isTask }: Ad
|
|||||||
onChange={(e) => setName(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<Popover>
|
<EmojiPickerButton
|
||||||
<PopoverTrigger asChild>
|
inputIdToFocus="name"
|
||||||
<Button
|
onEmojiSelect={(emoji) => {
|
||||||
type="button"
|
setName(prev => {
|
||||||
variant="ghost"
|
const space = prev.length > 0 && !prev.endsWith(' ') ? ' ' : '';
|
||||||
size="icon"
|
return `${prev}${space}${emoji}`;
|
||||||
className="h-8 w-8"
|
})
|
||||||
>
|
}}
|
||||||
<SmilePlus className="h-8 w-8" />
|
/>
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent className="w-[300px] p-0">
|
|
||||||
<Picker
|
|
||||||
data={data}
|
|
||||||
onEmojiSelect={(emoji: { native: string }) => {
|
|
||||||
setName(prev => {
|
|
||||||
// Add space before emoji if there isn't one already
|
|
||||||
const space = prev.length > 0 && !prev.endsWith(' ') ? ' ' : '';
|
|
||||||
return `${prev}${space}${emoji.native}`;
|
|
||||||
})
|
|
||||||
// Focus back on input after selection
|
|
||||||
const input = document.getElementById('name') as HTMLInputElement
|
|
||||||
input?.focus()
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
|
|||||||
@@ -9,12 +9,8 @@ import { Button } from '@/components/ui/button'
|
|||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
import { Textarea } from '@/components/ui/textarea'
|
import { Textarea } from '@/components/ui/textarea'
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
|
|
||||||
import { SmilePlus, Info } from 'lucide-react'
|
|
||||||
import data from '@emoji-mart/data'
|
|
||||||
import Picker from '@emoji-mart/react'
|
|
||||||
import { WishlistItemType } from '@/lib/types'
|
import { WishlistItemType } from '@/lib/types'
|
||||||
|
import EmojiPickerButton from './EmojiPickerButton'
|
||||||
import { MAX_COIN_LIMIT } from '@/lib/constants'
|
import { MAX_COIN_LIMIT } from '@/lib/constants'
|
||||||
|
|
||||||
interface AddEditWishlistItemModalProps {
|
interface AddEditWishlistItemModalProps {
|
||||||
@@ -114,7 +110,7 @@ export default function AddEditWishlistItemModal({
|
|||||||
} else {
|
} else {
|
||||||
addWishlistItem(itemData)
|
addWishlistItem(itemData)
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsOpen(false)
|
setIsOpen(false)
|
||||||
setEditingItem(null)
|
setEditingItem(null)
|
||||||
}
|
}
|
||||||
@@ -139,29 +135,15 @@ export default function AddEditWishlistItemModal({
|
|||||||
className="flex-1"
|
className="flex-1"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<Popover>
|
<EmojiPickerButton
|
||||||
<PopoverTrigger asChild>
|
inputIdToFocus="name"
|
||||||
<Button
|
onEmojiSelect={(emoji) => {
|
||||||
type="button"
|
setName(prev => {
|
||||||
variant="ghost"
|
const space = prev.length > 0 && !prev.endsWith(' ') ? ' ' : '';
|
||||||
size="icon"
|
return `${prev}${space}${emoji}`;
|
||||||
className="h-8 w-8"
|
})
|
||||||
>
|
}}
|
||||||
<SmilePlus className="h-4 w-4" />
|
/>
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent className="w-[300px] p-0">
|
|
||||||
<Picker
|
|
||||||
data={data}
|
|
||||||
onEmojiSelect={(emoji: { native: string }) => {
|
|
||||||
setName(prev => `${prev}${emoji.native}`)
|
|
||||||
// Focus back on input after selection
|
|
||||||
const input = document.getElementById('name') as HTMLInputElement
|
|
||||||
input?.focus()
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
@@ -296,13 +278,13 @@ export default function AddEditWishlistItemModal({
|
|||||||
<Avatar
|
<Avatar
|
||||||
key={user.id}
|
key={user.id}
|
||||||
className={`h-8 w-8 border-2 cursor-pointer
|
className={`h-8 w-8 border-2 cursor-pointer
|
||||||
${selectedUserIds.includes(user.id)
|
${selectedUserIds.includes(user.id)
|
||||||
? 'border-primary'
|
? 'border-primary'
|
||||||
: 'border-muted'
|
: 'border-muted'
|
||||||
}`}
|
}`}
|
||||||
title={user.username}
|
title={user.username}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedUserIds(prev =>
|
setSelectedUserIds(prev =>
|
||||||
prev.includes(user.id)
|
prev.includes(user.id)
|
||||||
? prev.filter(id => id !== user.id)
|
? prev.filter(id => id !== user.id)
|
||||||
: [...prev, user.id]
|
: [...prev, user.id]
|
||||||
|
|||||||
@@ -2,14 +2,16 @@
|
|||||||
|
|
||||||
import { ReactNode, useEffect } from 'react'
|
import { ReactNode, useEffect } from 'react'
|
||||||
import { useAtom } from 'jotai'
|
import { useAtom } from 'jotai'
|
||||||
import { pomodoroAtom, userSelectAtom } from '@/lib/atoms'
|
import { aboutOpenAtom, pomodoroAtom, userSelectAtom } from '@/lib/atoms'
|
||||||
import PomodoroTimer from './PomodoroTimer'
|
import PomodoroTimer from './PomodoroTimer'
|
||||||
import UserSelectModal from './UserSelectModal'
|
import UserSelectModal from './UserSelectModal'
|
||||||
import { useSession } from 'next-auth/react'
|
import { useSession } from 'next-auth/react'
|
||||||
|
import AboutModal from './AboutModal'
|
||||||
|
|
||||||
export default function ClientWrapper({ children }: { children: ReactNode }) {
|
export default function ClientWrapper({ children }: { children: ReactNode }) {
|
||||||
const [pomo] = useAtom(pomodoroAtom)
|
const [pomo] = useAtom(pomodoroAtom)
|
||||||
const [userSelect, setUserSelect] = useAtom(userSelectAtom)
|
const [userSelect, setUserSelect] = useAtom(userSelectAtom)
|
||||||
|
const [aboutOpen, setAboutOpen] = useAtom(aboutOpenAtom)
|
||||||
const { data: session, status } = useSession()
|
const { data: session, status } = useSession()
|
||||||
const currentUserId = session?.user.id
|
const currentUserId = session?.user.id
|
||||||
|
|
||||||
@@ -27,7 +29,10 @@ export default function ClientWrapper({ children }: { children: ReactNode }) {
|
|||||||
<PomodoroTimer />
|
<PomodoroTimer />
|
||||||
)}
|
)}
|
||||||
{userSelect && (
|
{userSelect && (
|
||||||
<UserSelectModal onClose={() => setUserSelect(false)}/>
|
<UserSelectModal onClose={() => setUserSelect(false)} />
|
||||||
|
)}
|
||||||
|
{aboutOpen && (
|
||||||
|
<AboutModal onClose={() => setAboutOpen(false)} />
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
51
components/EmojiPickerButton.tsx
Normal file
51
components/EmojiPickerButton.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { Button } from '@/components/ui/button'
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||||
|
import { SmilePlus } from 'lucide-react'
|
||||||
|
import data from '@emoji-mart/data'
|
||||||
|
import Picker from '@emoji-mart/react'
|
||||||
|
|
||||||
|
interface EmojiPickerButtonProps {
|
||||||
|
onEmojiSelect: (emoji: string) => void
|
||||||
|
inputIdToFocus?: string // Optional: ID of the input to focus after selection
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function EmojiPickerButton({ onEmojiSelect, inputIdToFocus }: EmojiPickerButtonProps) {
|
||||||
|
const [isEmojiPickerOpen, setIsEmojiPickerOpen] = useState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover modal={true} open={isEmojiPickerOpen} onOpenChange={setIsEmojiPickerOpen}>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="h-8 w-8" // Consistent sizing
|
||||||
|
>
|
||||||
|
<SmilePlus className="h-4 w-4" /> {/* Consistent icon size */}
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent
|
||||||
|
className="w-[300px] p-0"
|
||||||
|
onCloseAutoFocus={(event) => {
|
||||||
|
if (inputIdToFocus) {
|
||||||
|
event.preventDefault();
|
||||||
|
const input = document.getElementById(inputIdToFocus) as HTMLInputElement;
|
||||||
|
input?.focus();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Picker
|
||||||
|
data={data}
|
||||||
|
onEmojiSelect={(emoji: { native: string }) => {
|
||||||
|
onEmojiSelect(emoji.native);
|
||||||
|
setIsEmojiPickerOpen(false);
|
||||||
|
// Focus is handled by onCloseAutoFocus
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -16,7 +16,6 @@ import {
|
|||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@/components/ui/dropdown-menu'
|
} from '@/components/ui/dropdown-menu'
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
||||||
import AboutModal from './AboutModal'
|
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import dynamic from 'next/dynamic'
|
import dynamic from 'next/dynamic'
|
||||||
import { Profile } from './Profile'
|
import { Profile } from './Profile'
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { useAtom } from 'jotai'
|
|||||||
import { browserSettingsAtom } from '@/lib/atoms'
|
import { browserSettingsAtom } from '@/lib/atoms'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useTranslations } from 'next-intl'
|
import { useTranslations } from 'next-intl'
|
||||||
import AboutModal from './AboutModal'
|
|
||||||
import { HabitIcon, TaskIcon } from '@/lib/constants'
|
import { HabitIcon, TaskIcon } from '@/lib/constants'
|
||||||
import { useHelpers } from '@/lib/client-helpers'
|
import { useHelpers } from '@/lib/client-helpers'
|
||||||
|
|
||||||
@@ -71,7 +70,6 @@ export default function Navigation({ className, viewPort }: NavigationProps) {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<AboutModal isOpen={showAbout} onClose={() => setShowAbout(false)} />
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -97,7 +95,6 @@ export default function Navigation({ className, viewPort }: NavigationProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<AboutModal isOpen={showAbout} onClose={() => setShowAbout(false)} />
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle } from './ui/dialog'
|
|||||||
import UserForm from './UserForm'
|
import UserForm from './UserForm'
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
import { useAtom } from "jotai"
|
import { useAtom } from "jotai"
|
||||||
import { settingsAtom, userSelectAtom } from "@/lib/atoms"
|
import { aboutOpenAtom, settingsAtom, userSelectAtom } from "@/lib/atoms"
|
||||||
import AboutModal from "./AboutModal"
|
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
import { useTheme } from "next-themes"
|
import { useTheme } from "next-themes"
|
||||||
import { signOut } from "@/app/actions/user"
|
import { signOut } from "@/app/actions/user"
|
||||||
@@ -22,7 +21,7 @@ export function Profile() {
|
|||||||
const [settings] = useAtom(settingsAtom)
|
const [settings] = useAtom(settingsAtom)
|
||||||
const [userSelect, setUserSelect] = useAtom(userSelectAtom)
|
const [userSelect, setUserSelect] = useAtom(userSelectAtom)
|
||||||
const [isEditing, setIsEditing] = useState(false)
|
const [isEditing, setIsEditing] = useState(false)
|
||||||
const [showAbout, setShowAbout] = useState(false)
|
const [aboutOpen, setAboutOpen] = useAtom(aboutOpenAtom)
|
||||||
const { theme, setTheme } = useTheme()
|
const { theme, setTheme } = useTheme()
|
||||||
const { currentUser: user } = useHelpers()
|
const { currentUser: user } = useHelpers()
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
@@ -111,27 +110,32 @@ export function Profile() {
|
|||||||
</div>
|
</div>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem className="cursor-pointer px-2 py-1.5" asChild>
|
<DropdownMenuItem className="cursor-pointer px-2 py-1.5" asChild>
|
||||||
<Link
|
<div className="flex items-center justify-between w-full">
|
||||||
href="/settings"
|
<div className="flex items-center gap-2">
|
||||||
aria-label={t('settingsLink')}
|
<Settings className="h-4 w-4" />
|
||||||
className="flex items-center w-full gap-3"
|
<Link
|
||||||
>
|
href="/settings"
|
||||||
<Settings className="h-4 w-4" />
|
aria-label={t('settingsLink')}
|
||||||
<span>{t('settingsLink')}</span>
|
>
|
||||||
</Link>
|
<span>{t('settingsLink')}</span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem className="cursor-pointer px-2 py-1.5" asChild>
|
<DropdownMenuItem className="cursor-pointer px-2 py-1.5" onClick={() => {
|
||||||
<button
|
setOpen(false); // Close the dropdown
|
||||||
onClick={() => setShowAbout(true)}
|
setAboutOpen(true); // Open the about modal
|
||||||
className="flex items-center w-full gap-3"
|
}}>
|
||||||
>
|
<div className="flex items-center justify-between w-full">
|
||||||
<Info className="h-4 w-4" />
|
<div className="flex items-center gap-2">
|
||||||
<span>{t('aboutButton')}</span>
|
<Info className="h-4 w-4" />
|
||||||
</button>
|
<span>{t('aboutButton')}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem className="cursor-pointer px-2 py-1.5">
|
<DropdownMenuItem className="cursor-pointer px-2 py-1.5">
|
||||||
<div className="flex items-center justify-between w-full gap-3">
|
<div className="flex items-center justify-between w-full">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-2">
|
||||||
<Palette className="h-4 w-4" />
|
<Palette className="h-4 w-4" />
|
||||||
<span>{t('themeLabel')}</span>
|
<span>{t('themeLabel')}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -169,8 +173,6 @@ export function Profile() {
|
|||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|
||||||
<AboutModal isOpen={showAbout} onClose={() => setShowAbout(false)} />
|
|
||||||
|
|
||||||
{/* Add the UserForm dialog */}
|
{/* Add the UserForm dialog */}
|
||||||
{isEditing && user && (
|
{isEditing && user && (
|
||||||
<Dialog open={isEditing} onOpenChange={() => setIsEditing(false)}>
|
<Dialog open={isEditing} onOpenChange={() => setIsEditing(false)}>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const DialogOverlay = React.forwardRef<
|
|||||||
<DialogPrimitive.Overlay
|
<DialogPrimitive.Overlay
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:pointer-events-none",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ export const pomodoroAtom = atom<PomodoroAtom>({
|
|||||||
})
|
})
|
||||||
|
|
||||||
export const userSelectAtom = atom<boolean>(false)
|
export const userSelectAtom = atom<boolean>(false)
|
||||||
|
export const aboutOpenAtom = atom<boolean>(false)
|
||||||
|
|
||||||
// Derived atom for completion cache
|
// Derived atom for completion cache
|
||||||
export const completionCacheAtom = atom((get) => {
|
export const completionCacheAtom = atom((get) => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "habittrove",
|
"name": "habittrove",
|
||||||
"version": "0.2.16",
|
"version": "0.2.17",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbopack",
|
"dev": "next dev --turbopack",
|
||||||
|
|||||||
Reference in New Issue
Block a user