Merge pull request #113 from donetick/donetick.com-clean

Rebase to Donetick.com branch
This commit is contained in:
Mohamad Tarbin
2026-05-30 21:55:57 -04:00
committed by GitHub
36 changed files with 1819 additions and 925 deletions

View File

@@ -1,14 +1,18 @@
import NavBar from '@/views/components/NavBar'
import { Button, Typography, useColorScheme } from '@mui/joy'
import Tracker from '@openreplay/tracker'
import { useCallback, useEffect } from 'react'
import { Outlet } from 'react-router-dom'
import { useRegisterSW } from 'virtual:pwa-register/react'
import { registerCapacitorListeners } from './CapacitorListener'
import PageTransition from './components/animations/PageTransition'
import { ImpersonateUserProvider } from './contexts/ImpersonateUserContext'
import SSEProvider from './contexts/SSEContext'
import { AuthProvider } from './hooks/useAuth.jsx'
import useStatusBar from './hooks/useStatusBar'
import { useResource } from './queries/ResourceQueries'
import './styles/safe-area.css'
import SSEProvider from './contexts/SSEContext'
import { useNotification } from './service/NotificationProvider'
import NetworkBanner from './views/components/NetworkBanner'
@@ -24,18 +28,14 @@ const remove = className => {
// TODO: Update the interval to at 60 minutes
const intervalMS = 5 * 60 * 1000 // 5 minutes
const startOpenReplay = () => {
if (!import.meta.env.VITE_OPENREPLAY_PROJECT_KEY) return
const tracker = new Tracker({
projectKey: import.meta.env.VITE_OPENREPLAY_PROJECT_KEY,
})
tracker.start()
}
const AppContent = () => {
const { showNotification } = useNotification()
// Initialize status bar with theme-aware configuration
useStatusBar()
const {
offlineReady: [offlineReady, setOfflineReady], // eslint-disable-line no-unused-vars
needRefresh: [needRefresh, setNeedRefresh],
updateServiceWorker,
} = useRegisterSW({
@@ -94,10 +94,11 @@ const AppContent = () => {
}
function App() {
// startOpenReplay()
const resource = useResource() // eslint-disable-line no-unused-vars
const { mode, systemMode } = useColorScheme()
// startOpenReplay()
const setThemeClass = useCallback(() => {
const value = JSON.parse(localStorage.getItem('themeMode')) || mode
@@ -124,7 +125,7 @@ function App() {
}, [])
return (
<>
<div>
<NetworkBanner />
<AuthProvider>
@@ -132,7 +133,7 @@ function App() {
<AppContent />
</SSEProvider>
</AuthProvider>
</>
</div>
)
}

56
src/hooks/useStatusBar.js Normal file
View File

@@ -0,0 +1,56 @@
import { useColorScheme } from '@mui/joy'
import { useEffect } from 'react'
import statusBarManager from '../utils/StatusBarManager'
/**
* Custom hook to manage status bar integration with Joy UI themes
* This hook automatically syncs the status bar style with the current theme
*/
export const useStatusBar = () => {
const { mode, systemMode } = useColorScheme()
useEffect(() => {
// Initialize status bar on mount
const initializeStatusBar = async () => {
await statusBarManager.initialize(mode)
}
initializeStatusBar()
// Cleanup on unmount
return () => {
statusBarManager.cleanup()
}
}, [mode]) // Include mode dependency
useEffect(() => {
// Update status bar when theme changes
const updateStatusBarTheme = async () => {
let resolvedTheme = mode
// Handle system mode by using the detected system theme
if (mode === 'system') {
resolvedTheme = systemMode || 'light'
}
// Update the status bar with the resolved theme
await statusBarManager.updateResolvedTheme(resolvedTheme)
// Also update the base theme for future reference
await statusBarManager.setTheme(mode)
// Notify any custom listeners
statusBarManager.notifyThemeChange(resolvedTheme)
}
updateStatusBarTheme()
}, [mode, systemMode]) // Update when either mode or systemMode changes
return {
statusBarManager,
currentTheme: mode,
resolvedTheme: mode === 'system' ? systemMode : mode,
}
}
export default useStatusBar

127
src/styles/safe-area.css Normal file
View File

@@ -0,0 +1,127 @@
/*
* Safe Area CSS Utilities
* These utilities provide consistent safe area handling across the app
* using CSS custom properties set by StatusBarManager
*/
:root {
/* Fallback values for safe area insets when not on native platforms */
--safe-area-inset-top: 0px;
--safe-area-inset-right: 0px;
--safe-area-inset-bottom: 0px;
--safe-area-inset-left: 0px;
}
/* Utility classes for safe area handling */
.safe-area-top {
padding-top: var(--safe-area-inset-top);
}
.safe-area-right {
padding-right: var(--safe-area-inset-right);
}
.safe-area-bottom {
padding-bottom: var(--safe-area-inset-bottom);
}
.safe-area-left {
padding-left: var(--safe-area-inset-left);
}
.safe-area-x {
padding-left: var(--safe-area-inset-left);
padding-right: var(--safe-area-inset-right);
}
.safe-area-y {
padding-top: var(--safe-area-inset-top);
padding-bottom: var(--safe-area-inset-bottom);
}
.safe-area-all {
padding-top: var(--safe-area-inset-top);
padding-right: var(--safe-area-inset-right);
padding-bottom: var(--safe-area-inset-bottom);
padding-left: var(--safe-area-inset-left);
}
/* Margin variants */
.safe-margin-top {
margin-top: var(--safe-area-inset-top);
}
.safe-margin-right {
margin-right: var(--safe-area-inset-right);
}
.safe-margin-bottom {
margin-bottom: var(--safe-area-inset-bottom);
}
.safe-margin-left {
margin-left: var(--safe-area-inset-left);
}
.safe-margin-x {
margin-left: var(--safe-area-inset-left);
margin-right: var(--safe-area-inset-right);
}
.safe-margin-y {
margin-top: var(--safe-area-inset-top);
margin-bottom: var(--safe-area-inset-bottom);
}
.safe-margin-all {
margin-top: var(--safe-area-inset-top);
margin-right: var(--safe-area-inset-right);
margin-bottom: var(--safe-area-inset-bottom);
margin-left: var(--safe-area-inset-left);
}
/* Height utilities that account for safe areas */
.min-h-screen-safe {
min-height: calc(100vh - var(--safe-area-inset-top) - var(--safe-area-inset-bottom));
}
.h-screen-safe {
height: calc(100vh - var(--safe-area-inset-top) - var(--safe-area-inset-bottom));
}
/* Top positioning that accounts for safe area */
.top-safe {
top: var(--safe-area-inset-top);
}
/* Bottom positioning that accounts for safe area */
.bottom-safe {
bottom: var(--safe-area-inset-bottom);
}
/* Fixed positioning utilities that respect safe areas */
.fixed-top-safe {
position: fixed;
top: var(--safe-area-inset-top);
left: 0;
right: 0;
z-index: 1030;
}
.fixed-bottom-safe {
position: fixed;
bottom: var(--safe-area-inset-bottom);
left: 0;
right: 0;
z-index: 1030;
}
/* Container that provides full safe area coverage */
.container-safe {
padding-top: var(--safe-area-inset-top);
padding-right: var(--safe-area-inset-right);
padding-bottom: var(--safe-area-inset-bottom);
padding-left: var(--safe-area-inset-left);
min-height: 100vh;
box-sizing: border-box;
}

View File

@@ -26,7 +26,7 @@ class StatusBarManager {
}
try {
// Configure basic status bar settings - use overlay: true for precise control
// Configure basic status bar settings
await StatusBar.setOverlaysWebView({ overlay: false })
await StatusBar.show()

View File

@@ -1,14 +1,18 @@
import { Card, Grid, Typography } from '@mui/joy'
import moment from 'moment'
import { useState } from 'react'
import ChoreCard from '../Chores/ChoreCard'
const DemoMyChore = () => {
const [selectedCalendarDate, setSelectedCalendarDate] = useState(null)
const cards = [
{
id: 12,
name: '♻️ Take out recycle ',
frequencyType: 'days_of_the_week',
frequency: 1,
priority: 1,
frequencyMetadata:
'{"days":["thursday"],"time":"2024-07-07T22:00:00-04:00"}',
nextDueDate: moment().add(1, 'days').hour(8).minute(0).toISOString(),
@@ -96,6 +100,17 @@ const DemoMyChore = () => {
]
const users = [{ displayName: 'Me', id: 1, userId: 1 }]
// Helper function to get chores for a specific date
const getChoresForDate = date => {
return cards.filter(chore => {
if (!chore.nextDueDate) return false
const choreDate = new Date(chore.nextDueDate).toLocaleDateString()
const selectedDate = date.toLocaleDateString()
return choreDate === selectedDate
})
}
return (
<>
<Grid item xs={12} sm={5} data-aos-first-tasks-list>

View File

@@ -17,6 +17,10 @@ import TabletInstallationSection from './TabletInstallationSection'
const Landing = () => {
const Navigate = useNavigate()
useEffect(() => {
// if the host is https://app.donetick.com/ then redirect to https://app.donetick.com/my/chores:
if (window.location.host === 'app.donetick.com') {
Navigate('/chores')
}
AOS.init({
once: false, // whether animation should happen only once - while scrolling down
})

View File

@@ -1,5 +1,6 @@
import ipad_screenshot from '@/assets/ipad_dashbard_calendar.png'
import { Box, Container, Typography } from '@mui/joy'
const TabletInstallationSection = () => {
return (
<Container maxWidth='xl' sx={{ py: { xs: 6, sm: 8, md: 12 } }}>

View File

@@ -1,5 +1,3 @@
import React from 'react'
const PrivacyPolicyView = () => {
return (
<div>

View File

@@ -29,7 +29,10 @@ const AccountSettings = () => {
async function configurePurchases() {
if (Capacitor.isNativePlatform() && userProfile) {
await Purchases.configure({
apiKey: import.meta.env.VITE_REACT_APP_REVENUECAT_API_KEY,
apiKey:
Capacitor.getPlatform() === 'ios'
? import.meta.env.VITE_REACT_APP_REVENUECAT_API_KEY_IOS
: import.meta.env.VITE_REACT_APP_REVENUECAT_API_KEY_ANDROID,
appUserID: String(userProfile?.id),
})
}

View File

@@ -139,7 +139,10 @@ const Settings = () => {
async function configurePurchases() {
if (Capacitor.isNativePlatform() && userProfile) {
await Purchases.configure({
apiKey: import.meta.env.VITE_REACT_APP_REVENUECAT_API_KEY,
apiKey:
Capacitor.getPlatform() === 'ios'
? import.meta.env.VITE_REACT_APP_REVENUECAT_API_KEY_IOS
: import.meta.env.VITE_REACT_APP_REVENUECAT_API_KEY_ANDROID,
appUserID: String(userProfile?.id),
})
}

View File

@@ -221,7 +221,7 @@ const NavBar = () => {
},
}}
>
<div>
<div className='drawer-content'>
{/* <div className='align-center flex px-5 pt-4'>
<ModalClose size='sm' sx={{ top: 'unset', right: 20 }} />
</div> */}