diff --git a/src/components/auth/AuthRequiredModal.tsx b/src/components/auth/AuthRequiredModal.tsx index 495be0f8..fbeced95 100644 --- a/src/components/auth/AuthRequiredModal.tsx +++ b/src/components/auth/AuthRequiredModal.tsx @@ -18,7 +18,7 @@ export function AuthRequiredModal({ title = 'ورود به حساب کاربری', message = 'برای دسترسی به این بخش یا انجام این عملیات، ابتدا وارد حساب کاربری خود شوید.', loginButtonText = 'ورود به حساب', - cancelButtonText = 'بعداً', + cancelButtonText = 'بعدا', }: AuthRequiredModalProps) { function triggerAccountTabDisplay() { onClose() diff --git a/src/components/auth/require-auth.tsx b/src/components/auth/require-auth.tsx index 6165b69e..20b0b95f 100644 --- a/src/components/auth/require-auth.tsx +++ b/src/components/auth/require-auth.tsx @@ -1,4 +1,4 @@ -import { Motion as motion } from "@/common/motion"; +import { Motion as motion } from '@/common/motion' import type { ReactNode } from 'react' import { callEvent } from '@/common/utils/call-event' import { useAuth } from '@/context/auth.context' @@ -44,7 +44,7 @@ export const RequireAuth = ({ children, fallback, mode = 'block' }: RequireAuthP نیاز ورود به حساب کاربری

- برای دسترسی به این بخش، لطفاً وارد حساب کاربری خود شوید. + برای دسترسی به این بخش، لطفا وارد حساب کاربری خود شوید.

)} diff --git a/src/components/welcome-wizard.tsx b/src/components/welcome-wizard.tsx index fb03c628..95333790 100644 --- a/src/components/welcome-wizard.tsx +++ b/src/components/welcome-wizard.tsx @@ -91,7 +91,7 @@ export const WelcomeWizard = ({ isOpen, onClose }: WelcomeWizardProps) => { selectedInterests.length === 0 || !selectedReferralSource ) { - showToast('لطفاً تمام مراحل را تکمیل کنید.', 'error') + showToast('لطفا تمام مراحل را تکمیل کنید.', 'error') return } @@ -104,7 +104,7 @@ export const WelcomeWizard = ({ isOpen, onClose }: WelcomeWizardProps) => { }) ) if (err) { - showToast('خطا در ثبت اطلاعات. لطفاً دوباره تلاش کنید.', 'error') + showToast('خطا در ثبت اطلاعات. لطفا دوباره تلاش کنید.', 'error') Analytics.event('welcome_wizard_completion_failed') return } @@ -276,7 +276,7 @@ export const WelcomeWizard = ({ isOpen, onClose }: WelcomeWizardProps) => { مرحله ۴: از کجا شنیدی؟

- لطفاً بگو از کجا با ویجتیفای آشنا شدی. + لطفا بگو از کجا با ویجتیفای آشنا شدی.

diff --git a/src/layouts/bookmark/bookmark-grid.tsx b/src/layouts/bookmark/bookmark-grid.tsx index 16412331..250e9cb2 100644 --- a/src/layouts/bookmark/bookmark-grid.tsx +++ b/src/layouts/bookmark/bookmark-grid.tsx @@ -169,7 +169,6 @@ export function BookmarkGrid({ document.removeEventListener('click', handleClickOutside) } }, []) - // todo: padding: 0px 8px; return (
([]) @@ -143,12 +145,12 @@ export function BookmarksList() { } const bookmarkCount = currentFolderItems.length - const minBookmarks = 10 - const needsFillers = bookmarkCount < minBookmarks - const fillersCount = needsFillers ? minBookmarks - bookmarkCount : 0 + const maxBookmarks = 10 + const needsFillers = bookmarkCount < maxBookmarks + const fillersCount = needsFillers ? maxBookmarks - bookmarkCount : 0 const folderItems = [...currentFolderItems, ...new Array(fillersCount).fill(null)] - if (folderItems.length > minBookmarks) { + if (bookmarkCount >= maxBookmarks) { folderItems.push(null) } return folderItems @@ -206,9 +208,20 @@ export function BookmarksList() { addBookmark(bookmark, () => setShowAddBookmarkModal(false)) } parentId={currentFolderId} + onOpenImport={() => { + setShowAddBookmarkModal(false) + setShowImportBookmarksModal(true) + }} /> ) )} + {showImportBookmarksModal && ( + setShowImportBookmarksModal(false)} + parentId={currentFolderId} + /> + )} ) } diff --git a/src/layouts/bookmark/components/bookmark-suggestions.tsx b/src/layouts/bookmark/components/bookmark-suggestions.tsx index d56caa27..3958664d 100644 --- a/src/layouts/bookmark/components/bookmark-suggestions.tsx +++ b/src/layouts/bookmark/components/bookmark-suggestions.tsx @@ -23,13 +23,13 @@ export function BookmarkSuggestions({ onSelect }: BookmarkSuggestionsProps) {
onSelect(suggestion)} - className="p-2 flex flex-col items-center gap-y-0.5 text-center transition-colors duration-200 bg-content hover:!bg-base-300/75 border border-base-300/40 rounded-xl cursor-pointer" + className="p-1 flex flex-col items-center justify-center text-center transition-colors duration-200 bg-content hover:!bg-base-300/75 h-12 border border-base-300/40 rounded-xl cursor-pointer" > -
+
{suggestion.title} { const target = e.target as HTMLImageElement target.style.display = 'none' diff --git a/src/layouts/bookmark/components/modal/add-bookmark.modal.tsx b/src/layouts/bookmark/components/modal/add-bookmark.modal.tsx index 5adff506..846303be 100644 --- a/src/layouts/bookmark/components/modal/add-bookmark.modal.tsx +++ b/src/layouts/bookmark/components/modal/add-bookmark.modal.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from 'react' import { Button } from '@/components/button/button' import Modal from '@/components/modal' import { TextInput } from '@/components/text-input' +import { Icon } from '@/src/icons' import type { BookmarkType } from '../../types/bookmark.types' import { BookmarkSuggestions } from '../bookmark-suggestions' import { ShowAdvancedButton, TypeSelector } from '../shared' @@ -14,6 +15,7 @@ interface AddBookmarkModalProps { onClose: () => void onAdd: (bookmark: BookmarkCreateFormFields) => void parentId: string | null + onOpenImport?: () => void } export interface BookmarkCreateFormFields { @@ -50,6 +52,7 @@ export function AddBookmarkModal({ onClose, onAdd, parentId = null, + onOpenImport, }: AddBookmarkModalProps) { const [type, setType] = useState('BOOKMARK') const [showAdvanced, setShowAdvanced] = useState(false) @@ -180,7 +183,16 @@ export function AddBookmarkModal({ >
- + {onOpenImport && ( + + )}
لغو @@ -252,7 +262,7 @@ export function AddBookmarkModal({ isPrimary={true} loading={isAdding} className={ - 'btn btn-circle !w-fit px-8 border-none shadow-none text-secondary rounded-xl transition-colors duration-300 ease-in-out' + 'btn w-28 border-none shadow-none text-secondary rounded-2xl transition-colors duration-300 ease-in-out' } > ذخیره diff --git a/src/layouts/bookmark/components/modal/edit-bookmark.modal.tsx b/src/layouts/bookmark/components/modal/edit-bookmark.modal.tsx index 8a622fa1..603620ff 100644 --- a/src/layouts/bookmark/components/modal/edit-bookmark.modal.tsx +++ b/src/layouts/bookmark/components/modal/edit-bookmark.modal.tsx @@ -154,10 +154,10 @@ export function EditBookmarkModal({ size="md" title={`ویرایش ${type === 'FOLDER' ? 'پوشه' : 'بوکمارک'}`} direction="rtl" - className="!overflow-y-hidden" + className="overflow-y-hidden!" closeOnBackdropClick={false} > -
+
@@ -189,7 +189,7 @@ export function EditBookmarkModal({ } /> -
+
{type === 'BOOKMARK' && ( لغو @@ -232,9 +230,7 @@ export function EditBookmarkModal({ size="md" isPrimary={true} loading={isUpdating} - className={ - 'btn btn-circle !w-fit px-8 border-none shadow-none text-secondary !rounded-2xl transition-colors duration-300 ease-in-out' - } + className="transition-colors duration-300 ease-in-out border-none shadow-none btn w-28 text-secondary rounded-2xl" > ذخیره diff --git a/src/layouts/bookmark/components/modal/import-browser-bookmarks.modal.tsx b/src/layouts/bookmark/components/modal/import-browser-bookmarks.modal.tsx new file mode 100644 index 00000000..61ff111f --- /dev/null +++ b/src/layouts/bookmark/components/modal/import-browser-bookmarks.modal.tsx @@ -0,0 +1,373 @@ +import { useEffect, useMemo, useState } from 'react' +import Analytics from '@/analytics' +import { showToast } from '@/common/toast' +import { getFaviconFromUrl } from '@/common/utils/icon' +import { useGeneralSetting } from '@/context/general-setting.context' +import { Icon } from '@/src/icons' +import { Button } from '@/components/button/button' +import Modal from '@/components/modal' +import { useBookmarkStore } from '../../context/bookmark.context' +import { MAX_BROWSER_IMPORT_ITEMS } from '../../constants/browser-import.constant' +import type { BrowserImportNode } from '../../types/bookmark.types' +import { + type FetchedBrowserBookmark, + getBrowserBookmarks, +} from '../../utils/browser-bookmarks.util' + +interface ImportBrowserBookmarksModalProps { + isOpen: boolean + onClose: () => void + parentId: string | null + onImported?: () => void +} + +function collectDescendantIds(node: FetchedBrowserBookmark, acc: Set) { + acc.add(node.id) + node.children?.forEach((child) => collectDescendantIds(child, acc)) +} + +function buildImportNodes( + nodes: FetchedBrowserBookmark[], + selectedIds: Set +): BrowserImportNode[] { + const result: BrowserImportNode[] = [] + + for (const node of nodes) { + if (selectedIds.has(node.id)) { + if (node.type === 'FOLDER') { + result.push({ + title: node.title || 'پوشه بدون عنوان', + type: 'FOLDER', + url: null, + children: node.children + ? buildImportNodes(node.children, selectedIds) + : [], + }) + } else if (node.url) { + result.push({ + title: node.title || node.url, + type: 'BOOKMARK', + url: node.url, + }) + } + } else if (node.children?.length) { + result.push(...buildImportNodes(node.children, selectedIds)) + } + } + + return result +} + +interface TreeNodeProps { + node: FetchedBrowserBookmark + depth: number + selectedIds: Set + expandedIds: Set + onToggleSelect: (node: FetchedBrowserBookmark) => void + onToggleExpand: (id: string) => void +} + +function TreeNode({ + node, + depth, + selectedIds, + expandedIds, + onToggleSelect, + onToggleExpand, +}: TreeNodeProps) { + const isFolder = node.type === 'FOLDER' + const isExpanded = expandedIds.has(node.id) + const isChecked = selectedIds.has(node.id) + + const descendantIds = useMemo(() => { + const acc = new Set() + if (isFolder) collectDescendantIds(node, acc) + acc.delete(node.id) + return acc + }, [node, isFolder]) + + const selectedDescendantsCount = isFolder + ? [...descendantIds].filter((id) => selectedIds.has(id)).length + : 0 + const isIndeterminate = + isFolder && + !isChecked && + selectedDescendantsCount > 0 && + selectedDescendantsCount < descendantIds.size + + return ( +
+
onToggleSelect(node)} + > + {isFolder ? ( + + ) : ( + + )} + + + {(isChecked || isIndeterminate) && ( + + )} + + + {isFolder ? ( + + ) : ( + + )} + + + {node.title || 'بدون عنوان'} + +
+ + {isFolder && isExpanded && node.children && node.children.length > 0 && ( +
+ {node.children.map((child) => ( + + ))} +
+ )} +
+ ) +} + +export function ImportBrowserBookmarksModal({ + isOpen, + onClose, + parentId, + onImported, +}: ImportBrowserBookmarksModalProps) { + const { browserBookmarksEnabled, setBrowserBookmarksEnabled } = useGeneralSetting() + const { importBrowserBookmarks } = useBookmarkStore() + + const [rootNodes, setRootNodes] = useState([]) + const [selectedIds, setSelectedIds] = useState>(new Set()) + const [expandedIds, setExpandedIds] = useState>(new Set()) + const [isLoadingTree, setIsLoadingTree] = useState(false) + const [isImporting, setIsImporting] = useState(false) + + useEffect(() => { + if (!isOpen) return + + setSelectedIds(new Set()) + }, [isOpen]) + + useEffect(() => { + if (!isOpen || !browserBookmarksEnabled) return + + let cancelled = false + async function fetchTree() { + setIsLoadingTree(true) + const tree = await getBrowserBookmarks({ asTree: true }) + if (cancelled) return + + const initialNodes = tree[0]?.children?.length ? tree[0].children : tree + setRootNodes(initialNodes) + setExpandedIds(new Set(initialNodes.map((n) => n.id))) + setIsLoadingTree(false) + } + fetchTree() + + return () => { + cancelled = true + } + }, [isOpen, browserBookmarksEnabled]) + + const handlePermission = () => { + Analytics.event('browser_bookmark_import_permission_clicked') + setBrowserBookmarksEnabled(true) + } + + const handleToggleExpand = (id: string) => { + setExpandedIds((prev) => { + const next = new Set(prev) + if (next.has(id)) next.delete(id) + else next.add(id) + return next + }) + } + + const handleToggleSelect = (node: FetchedBrowserBookmark) => { + setSelectedIds((prev) => { + const ids = new Set() + collectDescendantIds(node, ids) + + const shouldSelect = !prev.has(node.id) + + if (shouldSelect) { + const newIds = [...ids].filter((id) => !prev.has(id)) + if (prev.size + newIds.length > MAX_BROWSER_IMPORT_ITEMS) { + showToast( + `حداکثر ${MAX_BROWSER_IMPORT_ITEMS} مورد در هر بار قابل انتخاب است.`, + 'error' + ) + return prev + } + + const next = new Set(prev) + newIds.forEach((id) => next.add(id)) + return next + } + + const next = new Set(prev) + ids.forEach((id) => next.delete(id)) + return next + }) + } + + const handleImport = async () => { + const importNodes = buildImportNodes(rootNodes, selectedIds) + if (importNodes.length === 0) { + showToast('حداقل یک بوکمارک را انتخاب کنید.', 'error') + return + } + + setIsImporting(true) + + const result = await importBrowserBookmarks(importNodes, parentId) + + setIsImporting(false) + + if (result && result.importedCount > 0) { + showToast(`${result.importedCount} مورد با موفقیت درون‌ریزی شد.`, 'success') + onImported?.() + onClose() + } else if (result) { + showToast('موردی برای درون‌ریزی یافت نشد.', 'error') + } + } + + return ( + + {!browserBookmarksEnabled ? ( +
+
+ +
+

دسترسی به بوکمارک‌ها

+

+ برای درون‌ریزی بوکمارک‌های مرورگر، نیاز به دسترسی شما داریم. +

+ +
+ ) : ( +
+
+ + {selectedIds.size > 0 + ? `${selectedIds.size} از ${MAX_BROWSER_IMPORT_ITEMS} مورد انتخاب شده` + : `حداکثر ${MAX_BROWSER_IMPORT_ITEMS} مورد قابل انتخاب است`} + +
+ +
+ {isLoadingTree ? ( +
+ +
+ ) : rootNodes.length > 0 ? ( + rootNodes.map((node) => ( + + )) + ) : ( +
+ بوکمارکی در مرورگر شما یافت نشد. +
+ )} +
+ +
+ + +
+
+ )} +
+ ) +} diff --git a/src/layouts/bookmark/constants/browser-import.constant.ts b/src/layouts/bookmark/constants/browser-import.constant.ts new file mode 100644 index 00000000..891f9113 --- /dev/null +++ b/src/layouts/bookmark/constants/browser-import.constant.ts @@ -0,0 +1 @@ +export const MAX_BROWSER_IMPORT_ITEMS = 50 diff --git a/src/layouts/bookmark/context/bookmark.context.tsx b/src/layouts/bookmark/context/bookmark.context.tsx index 1232faf2..76ef2a76 100644 --- a/src/layouts/bookmark/context/bookmark.context.tsx +++ b/src/layouts/bookmark/context/bookmark.context.tsx @@ -3,12 +3,14 @@ import React, { createContext, useEffect, useState } from 'react' import Analytics from '@/analytics' import { getFromStorage, setToStorage } from '@/common/storage' import { callEvent, listenEvent } from '@/common/utils/call-event' -import type { Bookmark } from '@/layouts/bookmark/types/bookmark.types' +import type { Bookmark, BrowserImportNode } from '@/layouts/bookmark/types/bookmark.types' import { safeAwait } from '@/services/api' import { useRemoveBookmark } from '@/services/hooks/bookmark/remove-bookmark.hook' import { translateError } from '@/utils/translate-error' import { useAuth } from '@/context/auth.context' import { useAddBookmark } from '@/services/hooks/bookmark/add-bookmark.hook' +import { useImportBrowserBookmarks } from '@/services/hooks/bookmark/import-browser-bookmarks.hook' +import type { BulkImportBookmarkNode } from '@/services/hooks/bookmark/import-browser-bookmarks.hook' import type { AxiosError } from 'axios' import type { BookmarkCreateFormFields } from '../components/modal/add-bookmark.modal' import type { BookmarkUpdateFormFields } from '../components/modal/edit-bookmark.modal' @@ -26,6 +28,10 @@ export interface BookmarkStoreContext { setBookmarks: (bookmarks: Bookmark[]) => void getCurrentFolderItems: (parentId: string | null) => Bookmark[] addBookmark: (bookmark: BookmarkCreateFormFields, cb: () => void) => Promise + importBrowserBookmarks: ( + nodes: BrowserImportNode[], + parentId: string | null + ) => Promise<{ importedCount: number; createdFolders: number } | null> editBookmark: (bookmark: BookmarkUpdateFormFields, cb: () => void) => void deleteBookmark: (id: string, cb: () => void) => void currentFolderId: string | null @@ -37,6 +43,7 @@ const bookmarkContext = createContext({ setBookmarks: () => {}, getCurrentFolderItems: () => [], addBookmark: async () => {}, + importBrowserBookmarks: async () => null, editBookmark: () => {}, deleteBookmark: () => {}, currentFolderId: null, @@ -53,6 +60,7 @@ export const BookmarkProvider: React.FC<{ children: React.ReactNode }> = ({ const { mutateAsync: removeBookmarkAsync } = useRemoveBookmark() const { mutateAsync: addBookmarkAsync } = useAddBookmark() + const { mutateAsync: importBrowserBookmarksAsync } = useImportBrowserBookmarks() const { mutateAsync: updateBookmarkAsync } = useUpdateBookmark() useEffect(() => { @@ -215,6 +223,62 @@ export const BookmarkProvider: React.FC<{ children: React.ReactNode }> = ({ } } + const importBrowserBookmarks = async ( + nodes: BrowserImportNode[], + parentId: string | null + ): Promise<{ importedCount: number; createdFolders: number } | null> => { + if (!isAuthenticated) { + showToast('برای درون‌ریزی بوکمارک‌ها باید وارد شوید.', 'error') + return null + } + + let resolvedParentId = parentId + if (parentId && validate(parentId)) { + const parentBookmark = bookmarks?.find( + (b) => b.id === parentId || b.onlineId === parentId + ) + if (parentBookmark?.onlineId) { + resolvedParentId = parentBookmark.onlineId + } + } + + if (resolvedParentId && validate(resolvedParentId)) { + showToast( + 'برای درون‌ریزی در این پوشه، لطفا ابتدا بوکمارک‌های خود را همگام‌سازی کنید.', + 'error', + { duration: 8000 } + ) + return null + } + + const toApiNode = (node: BrowserImportNode): BulkImportBookmarkNode => ({ + title: node.title, + type: node.type, + url: node.type === 'BOOKMARK' ? node.url : null, + children: node.children?.map(toApiNode), + }) + + const [err, result] = await safeAwait< + AxiosError, + { importedCount: number; createdFolders: number } + >( + importBrowserBookmarksAsync({ + parentId: resolvedParentId, + items: nodes.map(toApiNode), + }) + ) + + if (err || !result) { + autoFormatErrorToast(err) + return null + } + + await refetch() + Analytics.event('import_browser_bookmarks') + + return result + } + const editBookmark = async (input: BookmarkUpdateFormFields, cb: () => void) => { if (!isAuthenticated) return @@ -311,6 +375,7 @@ export const BookmarkProvider: React.FC<{ children: React.ReactNode }> = ({ setBookmarks, getCurrentFolderItems, addBookmark: addBookmark as any, + importBrowserBookmarks, editBookmark, deleteBookmark, currentFolderId, diff --git a/src/layouts/bookmark/types/bookmark.types.ts b/src/layouts/bookmark/types/bookmark.types.ts index 10773388..082116b4 100644 --- a/src/layouts/bookmark/types/bookmark.types.ts +++ b/src/layouts/bookmark/types/bookmark.types.ts @@ -23,3 +23,10 @@ export interface FolderPathItem { id: string title: string } + +export interface BrowserImportNode { + title: string + type: BookmarkType + url: string | null + children?: BrowserImportNode[] +} diff --git a/src/layouts/friends/components/activity-card/manage-activity.bottomSheet.tsx b/src/layouts/friends/components/activity-card/manage-activity.bottomSheet.tsx index e63fdac1..3d1ebfce 100644 --- a/src/layouts/friends/components/activity-card/manage-activity.bottomSheet.tsx +++ b/src/layouts/friends/components/activity-card/manage-activity.bottomSheet.tsx @@ -50,7 +50,7 @@ export function ManageActivityBottomSheet({ const handleSave = async () => { if (!activity.trim()) { - showToast('لطفاً متنی وارد کنید', 'error') + showToast('لطفا متنی وارد کنید', 'error') return } @@ -277,7 +277,7 @@ export function ManageActivityBottomSheet({ در صورت گزارش محتوای نامناسب، حساب کاربری شما ممکن است محدود شود.

-

لطفاً با رعایت قوانین به ما در حفظ فضای امن و دوستانه کمک کنید.

+

لطفا با رعایت قوانین به ما در حفظ فضای امن و دوستانه کمک کنید.

در هر روز فقط یک بار امکان انتشار نوشته جدید دارید.

diff --git a/src/layouts/navbar/friends-list/friends.navbar.tsx b/src/layouts/navbar/friends-list/friends.navbar.tsx index bf35879d..28512feb 100644 --- a/src/layouts/navbar/friends-list/friends.navbar.tsx +++ b/src/layouts/navbar/friends-list/friends.navbar.tsx @@ -94,7 +94,7 @@ export function FriendsListNavbar() { title="ورود به حساب کاربری" message="برای دسترسی به بخش مدیریت دوستان، ابتدا وارد حساب کاربری خود شوید." loginButtonText="ورود به حساب" - cancelButtonText="بعداً" + cancelButtonText="بعدا" /> )} diff --git a/src/layouts/search/image/image-search.portal.tsx b/src/layouts/search/image/image-search.portal.tsx index 3db346d2..febf59f3 100644 --- a/src/layouts/search/image/image-search.portal.tsx +++ b/src/layouts/search/image/image-search.portal.tsx @@ -27,7 +27,7 @@ export function ImageSearchPortal({ const handleUpload = async (file: File) => { if (!file.type.startsWith('image/')) { - showToast('لطفاً فقط فایل تصویری انتخاب کنید', 'error') + showToast('لطفا فقط فایل تصویری انتخاب کنید', 'error') return } diff --git a/src/layouts/setting/tabs/account/auth-form/auth-otp.tsx b/src/layouts/setting/tabs/account/auth-form/auth-otp.tsx index 636e8793..92031516 100644 --- a/src/layouts/setting/tabs/account/auth-form/auth-otp.tsx +++ b/src/layouts/setting/tabs/account/auth-form/auth-otp.tsx @@ -47,7 +47,7 @@ const AuthOtp: React.FC = ({ step, setStep }) => { if (isEmpty(email)) return setError((prev) => ({ ...prev, - email: 'لطفاً ایمیل/شماره موبایل خود را وارد کنید.', + email: 'لطفا ایمیل/شماره موبایل خود را وارد کنید.', })) onSendOtp() @@ -102,7 +102,7 @@ const AuthOtp: React.FC = ({ step, setStep }) => { if (err.response?.data?.message === 'INVALID_OTP_CODE') { setError({ email: null, - otp: 'کد تایید نامعتبر است. لطفاً دوباره تلاش کنید.', + otp: 'کد تایید نامعتبر است. لطفا دوباره تلاش کنید.', api: null, }) } else setError({ email: null, otp: null, api: content }) @@ -111,7 +111,7 @@ const AuthOtp: React.FC = ({ step, setStep }) => { setError({ email: null, otp: null, - api: 'کد تایید نامعتبر است. لطفاً دوباره تلاش کنید.', + api: 'کد تایید نامعتبر است. لطفا دوباره تلاش کنید.', }) return } diff --git a/src/layouts/setting/tabs/account/auth-form/auth-password.tsx b/src/layouts/setting/tabs/account/auth-form/auth-password.tsx index 3f95ee73..aa563d2c 100644 --- a/src/layouts/setting/tabs/account/auth-form/auth-password.tsx +++ b/src/layouts/setting/tabs/account/auth-form/auth-password.tsx @@ -30,13 +30,13 @@ export default function AuthPassword() { if (isEmpty(email)) return setError((prev) => ({ ...prev, - email: 'لطفاً ایمیل خود را وارد کنید.', + email: 'لطفا ایمیل خود را وارد کنید.', })) if (!isEmail(email)) return setError((prev) => ({ ...prev, - email: 'لطفاً یک ایمیل معتبر وارد کنید.', + email: 'لطفا یک ایمیل معتبر وارد کنید.', })) // Password validation diff --git a/src/layouts/setting/tabs/account/components/account-verification-status.tsx b/src/layouts/setting/tabs/account/components/account-verification-status.tsx index 5cf3e8c3..3298f303 100644 --- a/src/layouts/setting/tabs/account/components/account-verification-status.tsx +++ b/src/layouts/setting/tabs/account/components/account-verification-status.tsx @@ -23,7 +23,7 @@ export const AccountVerificationStatus = ({ ⚠️ حساب شما تایید نشده است

- لطفاً ایمیل خود را بررسی کنید یا ایمیل جدید درخواست کنید. + لطفا ایمیل خود را بررسی کنید یا ایمیل جدید درخواست کنید.

diff --git a/src/layouts/setting/tabs/account/components/modals/add-email.modal.tsx b/src/layouts/setting/tabs/account/components/modals/add-email.modal.tsx index 50961f4b..2e9b6163 100644 --- a/src/layouts/setting/tabs/account/components/modals/add-email.modal.tsx +++ b/src/layouts/setting/tabs/account/components/modals/add-email.modal.tsx @@ -45,7 +45,7 @@ export function AddEmailModal(prop: AddPhoneProp) { if (isEmpty(email)) return setError((prev) => ({ ...prev, - email: 'لطفاً ایمیل خود را وارد کنید.', + email: 'لطفا ایمیل خود را وارد کنید.', })) const [err, _] = await safeAwait(requestChange(email)) if (err) { diff --git a/src/layouts/setting/tabs/account/components/modals/add-phone.modal.tsx b/src/layouts/setting/tabs/account/components/modals/add-phone.modal.tsx index 58415e77..a8491df1 100644 --- a/src/layouts/setting/tabs/account/components/modals/add-phone.modal.tsx +++ b/src/layouts/setting/tabs/account/components/modals/add-phone.modal.tsx @@ -44,7 +44,7 @@ export function AddPhoneModal(prop: AddPhoneProp) { if (isEmpty(phone)) return setError((prev) => ({ ...prev, - email: 'لطفاً ایمیل/شماره موبایل خود را وارد کنید.', + email: 'لطفا ایمیل/شماره موبایل خود را وارد کنید.', })) const [err, _] = await safeAwait(requestChange(phone)) if (err) { diff --git a/src/layouts/setting/tabs/account/tabs/user-profile/user-profile.tsx b/src/layouts/setting/tabs/account/tabs/user-profile/user-profile.tsx index ff9607bf..72aea54b 100644 --- a/src/layouts/setting/tabs/account/tabs/user-profile/user-profile.tsx +++ b/src/layouts/setting/tabs/account/tabs/user-profile/user-profile.tsx @@ -54,7 +54,7 @@ export const UserProfile = () => { return 'نیاز به ورود مجدد به حساب کاربری دارید.' } - return 'خطا در بارگذاری پروفایل کاربری. لطفاً دوباره تلاش کنید.' + return 'خطا در بارگذاری پروفایل کاربری. لطفا دوباره تلاش کنید.' } if (isLoading) { diff --git a/src/layouts/setting/tabs/general/components/select-city.tsx b/src/layouts/setting/tabs/general/components/select-city.tsx index cddea537..f6a6426f 100644 --- a/src/layouts/setting/tabs/general/components/select-city.tsx +++ b/src/layouts/setting/tabs/general/components/select-city.tsx @@ -108,7 +108,7 @@ export function SelectCity({ size }: Prop) { خطا در دریافت اطلاعات
- لطفا اتصال اینترنت خود را بررسی کرده و مجدداً تلاش کنید. + لطفا اتصال اینترنت خود را بررسی کرده و مجددا تلاش کنید.
)} @@ -117,7 +117,7 @@ export function SelectCity({ size }: Prop) { setShowAuthModal(!showAuthModal)} - message="برای انتخاب شهر، لطفاً وارد حساب کاربری خود شوید." + message="برای انتخاب شهر، لطفا وارد حساب کاربری خود شوید." /> )} با فعال‌سازی این گزینه، افزونه فقط برای نمایش بوکمارک‌های مرورگر شما دسترسی خواهد داشت. هیچ اطلاعاتی ذخیره یا ارسال - نمی‌شود و بوکمارک‌ها صرفاً در محیط افزونه نمایش داده می‌شوند. + نمی‌شود و بوکمارک‌ها صرفا در محیط افزونه نمایش داده می‌شوند.

diff --git a/src/layouts/widgets/calendar/components/google-calendar/google-calendar-view.tsx b/src/layouts/widgets/calendar/components/google-calendar/google-calendar-view.tsx index 350b4440..7b286494 100644 --- a/src/layouts/widgets/calendar/components/google-calendar/google-calendar-view.tsx +++ b/src/layouts/widgets/calendar/components/google-calendar/google-calendar-view.tsx @@ -91,7 +91,7 @@ export const GoogleCalendarView: React.FC = () => {

{isAuthenticated ? 'برای مشاهده برنامه‌ها، تقویم گوگل را متصل کنید.' - : 'لطفاً وارد حساب کاربری خود شوید.'} + : 'لطفا وارد حساب کاربری خود شوید.'}