Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions background/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,13 @@ export function setupCaching() {
if (!isDev) {
registerRoute(
({ request }) =>
request.destination === 'script' ||
request.destination === 'style' ||
request.destination === 'font',
request.destination === 'script' || request.destination === 'style',
new CacheFirst({
cacheName: 'static-assets-v1',
plugins: [
new ExpirationPlugin({
maxEntries: 200,
maxAgeSeconds: 10 * 24 * 60 * 60, // 10 days
maxAgeSeconds: 10 * 24 * 60 * 60,
purgeOnQuotaError: true,
}),
new CacheableResponsePlugin({
Expand All @@ -72,13 +70,13 @@ export function setupCaching() {
)

registerRoute(
({ request }) => request.destination === 'image',
({ request }) => request.destination === 'font',
new CacheFirst({
cacheName: 'images-cache-v1',
cacheName: 'fonts-cache-v1',
plugins: [
new ExpirationPlugin({
maxEntries: 300,
maxAgeSeconds: 5 * 24 * 60 * 60, // 5 days
maxEntries: 50,
maxAgeSeconds: 2 * 60,
purgeOnQuotaError: true,
}),
new CacheableResponsePlugin({
Expand Down
1 change: 1 addition & 0 deletions src/common/constant/store.key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export interface StorageKV {
updatedAt: number
}[]
calendarDrawerState: boolean
recent_searches: any
pets: PetSettings
clock: ClockSettings
wigiPadDate: WigiPadDateSetting
Expand Down
2 changes: 1 addition & 1 deletion src/components/tab-navigation.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useId } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import { motion } from 'framer-motion'

interface TabItem<T> {
id: T
Expand Down
11 changes: 11 additions & 0 deletions src/context/appearance.context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { listenEvent } from '@/common/utils/call-event'
type UI = 'SIMPLE' | 'ADVANCED'
export interface AppearanceData {
fontFamily: string
contentAlignment: 'center' | 'top'
ui: UI
}
interface AppearanceContextContextType extends AppearanceData {
Expand All @@ -21,14 +22,17 @@ interface AppearanceContextContextType extends AppearanceData {
) => void
setFontFamily: (value: string) => void
setUI: (value: UI) => void
contentAlignment: 'center' | 'top'
canReOrderWidget: boolean
toggleCanReOrderWidget: () => void
ui: UI
setContentAlignment: (value: 'center' | 'top') => void
}

const DEFAULT_SETTINGS: AppearanceData = {
fontFamily: 'Vazir',
ui: 'ADVANCED',
contentAlignment: 'top',
}

export const AppearanceContext = createContext<AppearanceContextContextType | null>(null)
Expand Down Expand Up @@ -120,6 +124,11 @@ export function AppearanceProvider({ children }: { children: React.ReactNode })
}
}

const setContentAlignment = (value: 'center' | 'top') => {
updateSetting('contentAlignment', value)
Analytics.event(`set_content_alignment_${value}`)
}

const setUI = async (ui: UI) => {
if (!isAuthenticated)
return showToast(
Expand Down Expand Up @@ -154,12 +163,14 @@ export function AppearanceProvider({ children }: { children: React.ReactNode })

const contextValue: AppearanceContextContextType = {
fontFamily: settings.fontFamily,
contentAlignment: settings.contentAlignment,
updateSetting,
setFontFamily,
canReOrderWidget,
ui: settings.ui,
setUI: setUI,
toggleCanReOrderWidget,
setContentAlignment,
}

return (
Expand Down
106 changes: 106 additions & 0 deletions src/layouts/bookmark/components/bookmark-icon.picker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { showToast } from '@/common/toast'
import { getFaviconFromUrl } from '@/common/utils/icon'
import type React from 'react'
import { useRef, useState } from 'react'
import { FaUpload, FaImage } from 'react-icons/fa'
import { LuX } from 'react-icons/lu'

type Props = {
value: File | string | null
url?: string | null
onChange: (file: File | null) => void
}

export function BookmarkIconPicker({ value, url, onChange }: Props) {
const fileInputRef = useRef<HTMLInputElement>(null)
const [isDragging, setIsDragging] = useState(false)
const [error, setError] = useState(false)

const openPicker = () => {
fileInputRef.current?.click()
}

const handleFile = (file?: File) => {
if (!file || !file.type.startsWith('image/'))
return showToast('فرمت نامعتبر', 'error')
onChange(file)
}

const handleDrop = (e: React.DragEvent) => {
e.preventDefault()
setIsDragging(false)
handleFile(e.dataTransfer.files?.[0])
}

const handleUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
handleFile(e.target.files?.[0])
}

const handleRemove = (e: React.MouseEvent) => {
e.stopPropagation()
onChange(null)
setError(false)
}

const isFile = value instanceof File
let iconSrc: string | null = null
if (value && isFile) {
iconSrc = URL.createObjectURL(value)
} else {
if (value) {
iconSrc = value
} else if (url && url !== 'null') {
iconSrc = getFaviconFromUrl(url || '')
}
}

return (
<>
<input
ref={fileInputRef}
type="file"
hidden
accept="image/*"
onChange={handleUpload}
/>

<div
onClick={openPicker}
onDragOver={(e) => {
e.preventDefault()
setIsDragging(true)
}}
onDragLeave={() => setIsDragging(false)}
onDrop={handleDrop}
className={`relative w-12 h-12 flex items-center justify-center cursor-pointer border-2 rounded-xl transition
${isDragging ? 'border-blue-400' : 'border-base-300'}
`}
>
{iconSrc && !error ? (
<img
src={iconSrc}
className={`w-full h-full object-contain p-2 ${
isFile ? 'rounded-md' : 'rounded-xl'
}`}
onError={() => setError(true)}
/>
) : (
<FaImage />
)}

<div className="absolute inset-0 flex items-center justify-center transition opacity-0 hover:opacity-100">
<FaUpload />
</div>

{isFile && (
<button
onClick={handleRemove}
className="absolute top-0 right-0 flex items-center justify-center w-5 h-5 rounded-full bg-base-300"
>
<LuX size={12} />
</button>
)}
</div>
</>
)
}
2 changes: 1 addition & 1 deletion src/layouts/bookmark/components/bookmark/bookmark-icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export function BookmarkIcon({ bookmark }: { bookmark: Bookmark }) {
const colorClass = hasCustomColors ? '' : getColorFromTitle(bookmark.title)

return (
<div className="relative flex items-center justify-center w-4 h-4 sm:w-5 sm:h-5 md:w-8 md:h-8">
<div className="relative flex items-center justify-center w-4 h-4 sm:w-5 sm:h-5 md:w-9 md:h-9">
{typeof displayIcon === 'string' && !imageError ? (
<img
src={displayIcon}
Expand Down
4 changes: 2 additions & 2 deletions src/layouts/bookmark/components/bookmark/bookmark-title.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ export function BookmarkTitle({
customTextColor?: string
}) {
return (
<div className="mt-0.5 text-center w-20 truncate" dir="auto">
<div className="w-full px-1 text-center truncate" dir="auto">
<span
style={{ color: customTextColor || undefined, zIndex: 10 }}
className={`text-[.7rem] font-medium transition-colors duration-300 opacity-85 ${!customTextColor && 'text-content'} group-hover:opacity-100`}
className={`text-[.65rem] sm:text-[.7rem] md:text-[.75rem] font-medium transition-colors duration-300 opacity-85 block truncate ${!customTextColor && 'text-content'} group-hover:opacity-100`}
>
{title}
</span>
Expand Down
70 changes: 13 additions & 57 deletions src/layouts/bookmark/components/modal/add-bookmark.modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,10 @@ import Modal from '@/components/modal'
import { TextInput } from '@/components/text-input'
import type { BookmarkType } from '../../types/bookmark.types'
import { BookmarkSuggestions } from '../bookmark-suggestions'
import {
IconSourceSelector,
type IconSourceType,
ShowAdvancedButton,
TypeSelector,
useBookmarkIcon,
} from '../shared'
import { ShowAdvancedButton, TypeSelector } from '../shared'
import { AdvancedModal } from './advanced.modal'
import { useIsMutating } from '@tanstack/react-query'
import { getFaviconFromUrl } from '@/common/utils/icon'
import { BookmarkIconPicker } from '../bookmark-icon.picker'

interface AddBookmarkModalProps {
isOpen: boolean
Expand Down Expand Up @@ -58,7 +52,6 @@ export function AddBookmarkModal({
parentId = null,
}: AddBookmarkModalProps) {
const [type, setType] = useState<BookmarkType>('BOOKMARK')
const [iconSource, setIconSource] = useState<IconSourceType>('auto')
const [showAdvanced, setShowAdvanced] = useState(false)

const isAdding = useIsMutating({ mutationKey: ['addBookmark'] }) > 0
Expand All @@ -67,9 +60,6 @@ export function AddBookmarkModal({
structuredClone(empty)
)

const { fileInputRef, setIconLoadError, renderIconPreview, handleImageUpload } =
useBookmarkIcon()

const updateFormData: AddBookmarkUpdateFormData = <
K extends keyof BookmarkCreateFormFields,
>(
Expand All @@ -83,10 +73,7 @@ export function AddBookmarkModal({
const newUrl = value.trim()
updateFormData('url', newUrl)

if (iconSource === 'auto') {
setIconLoadError(false)
updateFormData('icon', null)
}
updateFormData('icon', null)

if (formData.title.trim() === '' && newUrl !== '') {
let hostName = ''
Expand Down Expand Up @@ -130,7 +117,6 @@ export function AddBookmarkModal({
const resetForm = () => {
setFormData(structuredClone(empty))
setType('BOOKMARK')
setIconSource('auto')
setShowAdvanced(false)
}

Expand All @@ -146,11 +132,6 @@ export function AddBookmarkModal({
}) => {
updateFormData('title', suggestion.title)
updateFormData('url', suggestion.url)

if (iconSource === 'auto') {
setIconLoadError(false)
updateFormData('icon', null)
}
}

const handleAdvancedModalClose = (
Expand Down Expand Up @@ -190,52 +171,27 @@ export function AddBookmarkModal({
size="md"
title={`${type === 'FOLDER' ? 'پوشه جدید' : 'بوکمارک جدید'}`}
direction="rtl"
className="!overflow-y-hidden"
className="overflow-y-hidden!"
closeOnBackdropClick={false}
>
<form
onSubmit={handleAdd}
className="flex flex-col justify-between gap-2 overflow-y-auto h-[24rem]"
className="flex flex-col justify-between gap-2 overflow-y-auto h-96"
>
<div className="mt-1 overflow-hidden">
<div className="flex h-8 gap-2 mb-2">
<TypeSelector type={type} setType={setType} />
</div>
<TypeSelector type={type} setType={setType} />

<div className="py-2 overflow-auto">
{' '}
<div
className={`mb-0.5 flex flex-row w-full items-center gap-y-2.5
${type === 'FOLDER' ? 'items-start justify-center' : 'items-center justify-between'}
className={`mb-0.5 flex flex-row w-full items-center gap-y-2.5 justify-center
`}
>
{type === 'BOOKMARK' && (
<IconSourceSelector
iconSource={iconSource}
setIconSource={setIconSource}
/>
)}
{renderIconPreview(
formData.icon ||
(formData.url && getFaviconFromUrl(formData.url)),
type === 'FOLDER' ? 'upload' : iconSource,
setIconSource,
(value) => updateFormData('icon', value)
)}
<BookmarkIconPicker
onChange={(value) => updateFormData('icon', value)}
value={formData.icon}
url={formData.url}
/>
</div>
<input
type="file"
ref={fileInputRef}
className="hidden"
accept="image/*"
onChange={(e) =>
handleImageUpload(
e,
(file) => updateFormData('icon', file),
setIconSource
)
}
/>
<TextInput
type="text"
name="title"
Expand All @@ -246,7 +202,7 @@ export function AddBookmarkModal({
'mt-2 w-full px-4 py-3 text-right rounded-lg transition-all duration-200 '
}
/>
<div className="relative h-[50px]">
<div className="relative h-12.5">
{type === 'BOOKMARK' && (
<TextInput
type="text"
Expand Down
Loading
Loading