diff --git a/src/App.tsx b/src/App.tsx index 8b90cb19..bdd5dd23 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,6 +3,7 @@ import { AppearanceProvider } from './context/appearance.context' import { AuthProvider } from './context/auth.context' import { ThemeProvider } from './context/theme.context' import { HomePage } from './pages/home' +import { PageProvider } from './context/page.context' const queryClient = new QueryClient({ defaultOptions: { @@ -18,7 +19,9 @@ function App() { - + + + diff --git a/src/common/constant/config.key.ts b/src/common/constant/config.key.ts index c154c5be..60d2a486 100644 --- a/src/common/constant/config.key.ts +++ b/src/common/constant/config.key.ts @@ -1,4 +1,4 @@ export enum ConfigKey { - VERSION_NAME = 'نسخه یلدا 🍉', + VERSION_NAME = 'امید', WIG_COIN_ICON = 'https://cdn.widgetify.ir/extension/wig-icon.png', } diff --git a/src/common/constant/moods.ts b/src/common/constant/moods.ts new file mode 100644 index 00000000..2a242520 --- /dev/null +++ b/src/common/constant/moods.ts @@ -0,0 +1,30 @@ +export const moodOptions = [ + { + value: 'sad', + emoji: '😔', + label: 'ناراحتم', + colorClass: 'error', + borderClass: 'border-error/50', + }, + { + value: 'tired', + emoji: '😴', + label: 'خستم', + colorClass: 'warning', + borderClass: 'border-yellow-400/50', + }, + { + value: 'happy', + emoji: '🙂', + label: 'اوکی‌ام', + colorClass: 'secondary', + borderClass: 'border-secondary/50', + }, + { + value: 'excited', + emoji: '😄', + label: 'سرحالم', + colorClass: 'success', + borderClass: 'border-green-400/50', + }, +] diff --git a/src/common/constant/priority_options.ts b/src/common/constant/priority_options.ts index 0b3c50fe..aa46f05e 100644 --- a/src/common/constant/priority_options.ts +++ b/src/common/constant/priority_options.ts @@ -13,7 +13,7 @@ export const PRIORITY_OPTIONS = [ }, { value: 'high', - ariaLabel: 'اولویت زیاد', + ariaLabel: 'اولویت مهم', bgColor: 'bg-red-500', hoverBgColor: 'hover:bg-red-500', }, diff --git a/src/common/constant/store.key.ts b/src/common/constant/store.key.ts index 1010ac28..02b9063d 100644 --- a/src/common/constant/store.key.ts +++ b/src/common/constant/store.key.ts @@ -89,4 +89,5 @@ export interface StorageKV { petState: boolean showNewBadgeForReOrderWidgets: boolean navbarVisible: boolean + [key: `removed_notification_${string}`]: string } diff --git a/src/common/storage.ts b/src/common/storage.ts index 6456b308..7b3a565f 100644 --- a/src/common/storage.ts +++ b/src/common/storage.ts @@ -57,3 +57,31 @@ export async function clearStorage() { export async function removeFromStorage(key: K) { await storage.removeItem(`local:${key}`) } + +export async function setWithExpiry( + key: K, + value: StorageKV[K], + minutes: number +) { + const expiry = Date.now() + minutes * 60000 + const data = { value, expiry } + + await setToStorage(key, data as any) +} + +export async function getWithExpiry( + key: K +): Promise { + const data = (await getFromStorage(key)) as any + + if (!data || typeof data !== 'object' || !('expiry' in data)) { + return data + } + + if (Date.now() > data.expiry) { + await removeFromStorage(key) + return null + } + + return data.value as StorageKV[K] +} diff --git a/src/common/utils/call-event.ts b/src/common/utils/call-event.ts index ce44c23b..56f485d8 100644 --- a/src/common/utils/call-event.ts +++ b/src/common/utils/call-event.ts @@ -11,6 +11,7 @@ import type { WidgetTabKeys } from '@/layouts/widgets-settings/constant/tab-keys import type { StoredWallpaper } from '../wallpaper.interface' import type { Todo } from '@/services/hooks/todo/todo.interface' import type { FontFamily } from '@/context/appearance.context' +import React from 'react' export interface EventName { startSync: SyncTarget @@ -31,8 +32,6 @@ export interface EventName { name: string template: string } - openExplorerPage: null - closeExplorerPage: null // setting keys wigiPadDateSettingsChanged: WigiPadDateSetting @@ -49,6 +48,9 @@ export interface EventName { font_change: FontFamily close_all_modals: null openWizardModal: null + add_to_notifications: { id: string; node: React.ReactNode } + remove_from_notifications: { id: string; ttl?: number } + go_to_page: 'explorer' | 'home' } export function callEvent(eventName: K, data?: EventName[K]) { diff --git a/src/components/UpdateReleaseNotesModal.tsx b/src/components/UpdateReleaseNotesModal.tsx index 2e1cea25..c5c6b8b4 100644 --- a/src/components/UpdateReleaseNotesModal.tsx +++ b/src/components/UpdateReleaseNotesModal.tsx @@ -27,45 +27,14 @@ const VERSION_NAME = ConfigKey.VERSION_NAME const releaseNotes: ReleaseNote[] = [ { - title: 'ویرایش وظایف', - type: 'feature', - description: - 'بالاخره اضافه شد! دیگه لازم نیست برای یه تغییر کوچیک، کل تسک رو پاک کنی و از اول بنویسی.', - }, - { - title: 'برنامه‌ریزی دقیق‌تر', - type: 'feature', - description: - 'حالا می‌تونی برای کارهات تاریخ و میزان اهمیت (اولویت) تعیین کنی تا هیچ چیزی یادت نره.', - }, - { - title: 'انتخاب راحت‌تر شهر', type: 'improvement', - description: - 'بخش آب‌وهوا رو جوری ردیف کردیم که خیلی سریع‌تر و راحت‌تر بتونی شهرت رو پیدا کنی.', + title: 'بهبود ظاهری', + description: 'ظاهر بعضی از قسمت هارو بهتر کردیم', }, { - title: 'خوشگل‌سازی منو', - type: 'improvement', - description: - 'منوی دسترسی پایین رو کمی دست‌کاری کردیم تا هم خوشگل‌تر بشه و هم کار کردن باهاش حال بده.', - }, - { - title: 'آب‌وهوای خلوت‌تر', - type: 'improvement', - description: - 'ویجت آب‌وهوا رو ساده کردیم تا فقط چیزایی که لازمه رو در یک نگاه ببینی.', - }, - { - title: 'بهبود فیلترهای صوتی', - type: 'improvement', - description: - 'بخش فیلترها و صداهای لیست وظایف رو بهینه کردیم تا حس بهتری موقع کار داشته باشی.', - }, - { - title: 'خداحافظی با فضای خالی', type: 'bugfix', - description: 'اون فضای خالی و اضافه پایین پنجره‌ها که رو مخ بود رو کلاً حذف کردیم.', + title: 'رفع چندین مشکل جزئی', + description: 'با سپاس از شما، مشکلات گزارش شده رو برطرف کردیم', }, ] @@ -124,18 +93,30 @@ export const UpdateReleaseNotesModal = ({ showCloseButton={false} >
-
-
-
-

- {VERSION_NAME} -

-

- تغییرات جدید برای طولانی‌ترین شب سال -

-
-
- +
+
+ +
+
+
+

+ {VERSION_NAME} +

+

+ آدمیزاد به امید زندسـت! 🤞💙{' '} +

+
@@ -145,7 +126,7 @@ export const UpdateReleaseNotesModal = ({ {releaseNotes.map((note, index) => (
@@ -164,11 +145,9 @@ export const UpdateReleaseNotesModal = ({ ))}
-
+
- - دمت گرم که همراه مایی - + دمت گرم که همراه مایی
@@ -177,7 +156,7 @@ export const UpdateReleaseNotesModal = ({ href="https://feedback.widgetify.ir" target="_blank" rel="noreferrer" - className="text-[10px] italic font-black text-muted hover:text-content transition-all underline decoration-dotted underline-offset-4" + className="text-[10px] font-black text-muted hover:text-content transition-all underline decoration-dotted underline-offset-4" > پیشنهاد یا گزارش مشکل @@ -185,7 +164,7 @@ export const UpdateReleaseNotesModal = ({ size="sm" onClick={onClose} disabled={counter > 0} - className="min-w-[130px] h-11 !rounded-2xl font-black italic text-[11px] shadow-lg shadow-primary/10 disabled:shadow-none active:scale-90 transition-all disabled:text-base-content/30" + className="min-w-[130px] h-11 !rounded-2xl font-black text-xs shadow-lg shadow-primary/10 disabled:shadow-none active:scale-90 transition-all disabled:text-base-content/30" isPrimary={true} > {counter > 0 ? `یه چند لحظه صبر کن (${counter})` : 'فهمیدم'} diff --git a/src/components/tab-navigation.tsx b/src/components/tab-navigation.tsx new file mode 100644 index 00000000..e7977d3d --- /dev/null +++ b/src/components/tab-navigation.tsx @@ -0,0 +1,73 @@ +import type React from 'react' + +interface TabItem { + id: T + label: string + icon?: React.ReactNode +} + +interface TabNavigationProps { + tabs: TabItem[] + activeTab: T | null + onTabClick: (tab: T) => void + size?: 'small' | 'medium' | 'large' + className?: string +} + +export const TabNavigation = ({ + tabs, + activeTab, + onTabClick, + size = 'medium', + className = '', +}: TabNavigationProps) => { + const sizeClasses = { + small: 'py-1 px-2 text-[10px]', + medium: 'py-2 px-1 text-xs', + large: 'py-3 px-2 text-sm', + } + + return ( +
+ {tabs.map((tab) => { + const isActive = activeTab === tab.id + + return ( + + ) + })} +
+ ) +} diff --git a/src/context/page.context.tsx b/src/context/page.context.tsx new file mode 100644 index 00000000..c946ceaf --- /dev/null +++ b/src/context/page.context.tsx @@ -0,0 +1,39 @@ +import { listenEvent } from '@/common/utils/call-event' +import type React from 'react' +import { createContext, useContext, useState } from 'react' + +type Page = 'home' | 'explorer' +interface PageContextType { + page: Page + setPage: (page: Page) => void +} + +export const PageContext = createContext(null) +export function PageProvider({ children }: { children: React.ReactNode }) { + const [page, setPage] = useState('home') + + useEffect(() => { + const event = listenEvent('go_to_page', (p) => { + console.log('listenEvent', p) + setPage(p) + }) + console.log('listenEvent go_to_page') + return () => { + event() + } + }, []) + + return ( + {children} + ) +} + +export function usePage() { + const context = useContext(PageContext) + + if (!context) { + throw new Error('usePage must be used within a PageProvider') + } + + return context +} diff --git a/src/layouts/bookmark/components/shared.tsx b/src/layouts/bookmark/components/shared.tsx index 3969175e..0282ade2 100644 --- a/src/layouts/bookmark/components/shared.tsx +++ b/src/layouts/bookmark/components/shared.tsx @@ -4,6 +4,8 @@ import { FiChevronUp } from 'react-icons/fi' import { LuX } from 'react-icons/lu' import type { BookmarkType } from '../types/bookmark.types' import { showToast } from '@/common/toast' +import { HiOutlineBookmark, HiOutlineFolder } from 'react-icons/hi2' +import { TabNavigation } from '@/components/tab-navigation' export type IconSourceType = 'auto' | 'upload' | 'url' @@ -15,29 +17,16 @@ export function IconSourceSelector({ setIconSource: (source: IconSourceType) => void theme?: string }) { - const getButtonStyle = (isActive: boolean) => { - if (isActive) { - return 'bg-primary text-white' - } - - return 'text-content' - } - return ( -
-
setIconSource('auto')} - className={`px-3 py-1 cursor-pointer rounded-xl transition-all duration-300 ${getButtonStyle(iconSource === 'auto')}`} - > - آیکون خودکار -
-
setIconSource('upload')} - className={`px-3 py-1 cursor-pointer rounded-xl transition-all duration-300 ${getButtonStyle(iconSource === 'upload')}`} - > - آپلود آیکون -
-
+ + size="small" + tabs={[ + { id: 'auto', label: 'آیکون خودکار' }, + { id: 'upload', label: 'آپلود آیکون' }, + ]} + activeTab={iconSource} + onTabClick={(tab) => setIconSource(tab)} + /> ) } @@ -47,33 +36,22 @@ export function TypeSelector({ }: { type: BookmarkType setType: (type: BookmarkType) => void - theme?: string }) { return ( - <> - - - + + className="w-full!" + size="small" + tabs={[ + { + id: 'BOOKMARK', + label: 'بوکمارک', + icon: , + }, + { id: 'FOLDER', label: 'پوشه', icon: }, + ]} + activeTab={type} + onTabClick={(tab) => setType(tab)} + /> ) } diff --git a/src/layouts/explorer/components/content-iframe.tsx b/src/layouts/explorer/components/content-iframe.tsx index 78e6246f..5850fb03 100644 --- a/src/layouts/explorer/components/content-iframe.tsx +++ b/src/layouts/explorer/components/content-iframe.tsx @@ -29,7 +29,6 @@ export function RenderContentIframe({ link, theme, fontFamily }: IframeProp) { height={link.height} style={{ border: 'none', - borderRadius: '1.5rem', width: '100%', }} title={link.name || link.url} diff --git a/src/layouts/explorer/components/content-site.tsx b/src/layouts/explorer/components/content-site.tsx index 45b7e215..f35cb618 100644 --- a/src/layouts/explorer/components/content-site.tsx +++ b/src/layouts/explorer/components/content-site.tsx @@ -26,14 +26,14 @@ export function RenderContentSite({ link }: SiteProp) { href={getUrl(link.url)} target="_blank" rel="noopener noreferrer" - className={` flex flex-col items-center gap-1 transition-all duration-500 group active:scale-95 ${pos} rounded-2xl`} + className={`flex flex-col items-center gap-1 transition-all duration-500 group active:scale-95 ${pos} rounded-2xl hover:bg-base-300 group-hover:shadow-sm p-0.5`} style={{ gridColumn: col ? `span ${col} / span ${col}` : undefined, gridRow: row ? `span ${row} / span ${row}` : undefined, }} > {link.icon && ( -
+
{badge && ( + {[1, 2, 3, 4, 5, 6].map((i) => ( +
+
+
+
+
+
+ {[1, 2, 3, 4, 5, 6, 7, 8].map((j) => ( +
+
+
+
+ ))} +
+
+ ))} +
+ ) +} + export function ExplorerContent() { const { theme } = useTheme() const { fontFamily } = useAppearanceSetting() - const { data: catalogData } = useGetContents() + const { data: catalogData, isLoading } = useGetContents() const [activeCategory, setActiveCategory] = useState(null) const categoryRefs = useRef<{ [key: string]: HTMLDivElement | null }>({}) const scrollContainerRef = useRef(null) @@ -57,66 +83,84 @@ export function ExplorerContent() { return (
-