Compare commits

..

8 Commits

Author SHA1 Message Date
da593e052b Update PWA version for focus improvements
- Bumped service worker cache to v8
- Updated manifest version to 1.0.2
- Forces PWA refresh to pick up keyboard focus improvements
2025-12-14 01:01:22 +00:00
37955f95ab Keep input focused and keyboard open after adding tasks
- Added useRef to manage input focus directly
- Added useEffect to auto-focus when form becomes visible
- Refocus input after successful task creation with setTimeout
- Modified handleBlur to preserve focus when keeping form open
- Provides seamless continuous task input experience on mobile
2025-12-14 00:59:48 +00:00
466bce862e Update PWA manifest and service worker to force iPhone refresh
- Updated manifest.json version to 1.0.1 and description
- Bumped service worker cache version from v6 to v7
- Forces PWA refresh on iPhone Safari to pick up latest changes
2025-12-14 00:56:29 +00:00
3043293fa7 Keep task form open after adding task for continuous input
- Modified InlineItemCreate component to maintain form visibility after successful task creation
- Added keepFormOpen state to prevent form from closing on page revalidation
- Users can now add multiple tasks without clicking 'Add task' button repeatedly
- Form only closes when explicitly cancelled or escaped
2025-12-14 00:40:59 +00:00
1838502b8b fix: prevent hydration error in NotificationPrompt
Add mounted state to prevent SSR rendering.
Component now only renders after client-side mount.
Fixes login blocking hydration error.
2025-12-07 01:59:55 +00:00
6508e27391 chore: remove NotificationDebug component
Debug complete - notifications are working (permission already granted).
Remove debug component to clean up UI.
2025-12-07 01:58:39 +00:00
15b9f196f5 chore: bump service worker cache version to v6
Force update on all PWA installations to pick up:
- NotificationPrompt component
- NotificationDebug component
- Push notification functionality
- Hydration fix
2025-12-07 01:55:28 +00:00
1c5e623c7b fix: prevent hydration error in NotificationDebug component
Add mounted state to prevent rendering during SSR.
Component now only renders after client-side mount.
2025-12-07 01:53:52 +00:00
6 changed files with 57 additions and 16 deletions

View File

@@ -3,7 +3,6 @@ import './globals.css'
import ServiceWorkerRegistration from '@/components/ServiceWorkerRegistration'
import OfflineIndicator from '@/components/OfflineIndicator'
import NotificationPrompt from '@/components/NotificationPrompt'
import NotificationDebug from '@/components/NotificationDebug'
import { ToastProvider } from '@/components/ToastContainer'
import ErrorBoundary from '@/components/ErrorBoundary'
@@ -78,7 +77,6 @@ export default function RootLayout({
<ServiceWorkerRegistration />
<OfflineIndicator />
<NotificationPrompt />
<NotificationDebug />
{children}
</ToastProvider>
</ErrorBoundary>

View File

@@ -1,6 +1,6 @@
'use client'
import { useState } from 'react'
import { useState, useRef, useEffect } from 'react'
import { useRouter } from 'next/navigation'
import { createItem } from '@/lib/actions'
import { Plus } from 'lucide-react'
@@ -18,6 +18,20 @@ export default function InlineItemCreate({ listId }: InlineItemCreateProps) {
const [title, setTitle] = useState('')
const [isSubmitting, setIsSubmitting] = useState(false)
const [error, setError] = useState('')
const [keepFormOpen, setKeepFormOpen] = useState(false)
const inputRef = useRef<HTMLInputElement>(null)
// Focus input when form becomes visible
useEffect(() => {
if ((isCreating || keepFormOpen) && inputRef.current) {
// Small delay to ensure the input is rendered
setTimeout(() => {
if (inputRef.current) {
inputRef.current.focus()
}
}, 50)
}
}, [isCreating, keepFormOpen])
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
@@ -40,14 +54,21 @@ export default function InlineItemCreate({ listId }: InlineItemCreateProps) {
// Clear form immediately for better UX
const itemTitle = sanitizeString(title)
setTitle('')
setIsCreating(false)
setKeepFormOpen(true) // Mark that we want to keep the form open
// Create item and refresh
await createItem(listId, itemTitle)
showToast('Task added successfully', 'success')
// Force immediate refresh
router.refresh()
// The revalidatePath in createItem will refresh the data
// but we'll keep the form open due to keepFormOpen state
// Refocus the input after a short delay to keep keyboard open
setTimeout(() => {
if (inputRef.current) {
inputRef.current.focus()
}
}, 100)
} catch (error) {
console.error('Error creating item:', error)
const errorMessage = error instanceof Error ? error.message : 'Failed to add task'
@@ -63,6 +84,7 @@ export default function InlineItemCreate({ listId }: InlineItemCreateProps) {
const handleCancel = () => {
setTitle('')
setIsCreating(false)
setKeepFormOpen(false) // Reset the keep form open state
// Reset zoom on iOS Safari
if (document.activeElement instanceof HTMLElement) {
document.activeElement.blur()
@@ -75,12 +97,16 @@ export default function InlineItemCreate({ listId }: InlineItemCreateProps) {
}
}
const handleBlur = () => {
// Force viewport reset on iOS Safari
window.scrollTo(0, 0)
const handleBlur = (e: React.FocusEvent) => {
// Only handle blur if we're not keeping the form open
// This prevents the keyboard from closing when we want it to stay open
if (!keepFormOpen) {
// Force viewport reset on iOS Safari
window.scrollTo(0, 0)
}
}
if (!isCreating) {
if (!isCreating && !keepFormOpen) {
return (
<button
onClick={() => setIsCreating(true)}
@@ -96,6 +122,7 @@ export default function InlineItemCreate({ listId }: InlineItemCreateProps) {
<form onSubmit={handleSubmit} className="bg-white border-2 border-blue-400 rounded-lg p-4 sm:p-5 shadow-sm">
<div className="mb-3 sm:mb-4">
<input
ref={inputRef}
type="text"
value={title}
onChange={(e) => {

View File

@@ -3,9 +3,11 @@
import { useEffect, useState } from 'react';
export default function NotificationDebug() {
const [mounted, setMounted] = useState(false);
const [debug, setDebug] = useState<any>({});
useEffect(() => {
setMounted(true);
const info = {
notificationSupported: 'Notification' in window,
serviceWorkerSupported: 'serviceWorker' in navigator,
@@ -16,6 +18,10 @@ export default function NotificationDebug() {
setDebug(info);
}, []);
if (!mounted) {
return null;
}
return (
<div className="fixed bottom-4 left-4 bg-black text-white text-xs p-3 rounded-lg max-w-xs z-50 font-mono">
<div className="font-bold mb-2">Push Debug:</div>

View File

@@ -8,8 +8,15 @@ export default function NotificationPrompt() {
const { permission, isSubscribed, subscribe } = usePushNotifications();
const [showPrompt, setShowPrompt] = useState(false);
const [dismissed, setDismissed] = useState(false);
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
useEffect(() => {
if (!mounted) return;
// Check if we should show the prompt
const shouldShow =
'Notification' in window &&
@@ -27,7 +34,7 @@ export default function NotificationPrompt() {
return () => clearTimeout(timer);
}
}, [permission, isSubscribed, dismissed]);
}, [mounted, permission, isSubscribed, dismissed]);
const handleEnable = async () => {
const success = await subscribe();
@@ -42,12 +49,14 @@ export default function NotificationPrompt() {
};
const handleDismissForever = () => {
localStorage.setItem('notification-prompt-dismissed', 'true');
if (typeof localStorage !== 'undefined') {
localStorage.setItem('notification-prompt-dismissed', 'true');
}
setShowPrompt(false);
setDismissed(true);
};
if (!showPrompt) {
if (!mounted || !showPrompt) {
return null;
}

View File

@@ -1,7 +1,8 @@
{
"name": "Ultimate Lists",
"short_name": "Lists",
"description": "The ultimate todo list app with advanced task management",
"description": "The ultimate todo list app with advanced task management and continuous task input",
"version": "1.0.2",
"start_url": "/",
"scope": "/",
"display": "standalone",

View File

@@ -1,8 +1,8 @@
// Service Worker for Ultimate Lists App
// Implements caching strategies for offline functionality
const CACHE_NAME = 'ultimate-lists-v5';
const RUNTIME_CACHE = 'ultimate-lists-runtime-v5';
const CACHE_NAME = 'ultimate-lists-v8';
const RUNTIME_CACHE = 'ultimate-lists-runtime-v8';
// Static assets to cache on install
const STATIC_ASSETS = [