Skip to content

Feat: Set Folder Password #175

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
49 changes: 37 additions & 12 deletions src/layouts/bookmark/bookmarks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { AddBookmarkModal } from './components/modal/add-bookmark.modal'
import { BookmarkContextMenu } from './components/modal/bookmark-context-menu'
import { EditBookmarkModal } from './components/modal/edit-bookmark.modal'
import type { Bookmark, FolderPathItem } from './types/bookmark.types'
import { FolderPasswordModal } from './components/modal/folder-password.modal'

export function BookmarksComponent() {
const {
Expand All @@ -24,19 +25,17 @@ export function BookmarksComponent() {

const [showAddBookmarkModal, setShowAddBookmarkModal] = useState(false)
const [showEditBookmarkModal, setShowEditBookmarkModal] = useState(false)
const [showFolderPasswordModal, setShowFolderPasswordModal] = useState(false)
const [bookmarkToEdit, setBookmarkToEdit] = useState<Bookmark | null>(null)
const [folderPassword, setFolderPassword] = useState('')
const [folderToOpen, setFolderToOpen] = useState<Bookmark | null>(null)

const [selectedBookmark, setSelectedBookmark] = useState<Bookmark | null>(null)

const [contextMenuPos, setContextMenuPos] = useState({ x: 0, y: 0 })

const [currentFolderId, setCurrentFolderId] = useState<string | null>(null)

const [folderPath, setFolderPath] = useState<FolderPathItem[]>([])

const [currentFolderIsManageable, setCurrentFolderIsManageable] =
useState<boolean>(true)

const [draggedBookmarkId, setDraggedBookmarkId] = useState<string | null>(null)
const [dragOverIndex, setDragOverIndex] = useState<number | null>(null)

Expand Down Expand Up @@ -93,6 +92,21 @@ export function BookmarksComponent() {
setShowEditBookmarkModal(true)
setSelectedBookmark(null)
}

const handleOpenFolder = (bookmark: Bookmark, e?: React.MouseEvent<any>) => {
if (e?.ctrlKey || e?.metaKey) {
openBookmarks(bookmark)
} else {
setCurrentFolderId(bookmark.id)
setFolderPath([
...folderPath,
{ id: bookmark.id, title: bookmark.title, password: bookmark.password },
])

setCurrentFolderIsManageable(isManageable(bookmark))
}
}

const handleBookmarkClick = (bookmark: Bookmark, e?: React.MouseEvent<any>) => {
if (e) {
e.preventDefault()
Expand All @@ -110,13 +124,12 @@ export function BookmarksComponent() {
}

if (bookmark.type === 'FOLDER') {
if (e?.ctrlKey || e?.metaKey) {
openBookmarks(bookmark)
setFolderPassword(bookmark.password ?? '')
if (bookmark.password) {
setFolderToOpen(bookmark)
setShowFolderPasswordModal(true)
} else {
setCurrentFolderId(bookmark.id)
setFolderPath([...folderPath, { id: bookmark.id, title: bookmark.title }])

setCurrentFolderIsManageable(isManageable(bookmark))
handleOpenFolder(bookmark, e)
}
} else {
if (e?.ctrlKey || e?.metaKey) {
Expand Down Expand Up @@ -315,7 +328,11 @@ export function BookmarksComponent() {
bookmark ? (
<div
key={i}
className={`transition-transform duration-200 ${dragOverIndex === i ? 'scale-110 border-2 border-blue-400 rounded-full' : 'rounded-full'}`}
className={`transition-transform duration-200 ${
dragOverIndex === i
? 'scale-110 border-2 border-blue-400 rounded-full'
: 'rounded-full'
}`}
>
{bookmark.type === 'FOLDER' ? (
<FolderBookmarkItem
Expand Down Expand Up @@ -376,6 +393,14 @@ export function BookmarksComponent() {
<div className="flex justify-center w-full mt-2">
<FolderPath folderPath={folderPath} onNavigate={handleNavigate} />
</div>
<FolderPasswordModal
onConfirm={() => {
if (folderToOpen) handleOpenFolder(folderToOpen)
}}
folderPassword={folderPassword}
isOpen={showFolderPasswordModal}
onClose={() => setShowFolderPasswordModal(false)}
/>
<AddBookmarkModal
isOpen={showAddBookmarkModal}
onClose={() => setShowAddBookmarkModal(false)}
Expand Down
13 changes: 11 additions & 2 deletions src/layouts/bookmark/components/bookmark-folder.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState } from 'react'
import { FaFolder, FaFolderOpen } from 'react-icons/fa'
import { LuFolderLock } from 'react-icons/lu'
import { SlOptions } from 'react-icons/sl'
import { addOpacityToColor } from '@/common/color'
import Tooltip from '@/components/toolTip'
Expand Down Expand Up @@ -37,7 +38,11 @@ export function FolderBookmarkItem({
const displayIcon =
bookmark.customImage ||
(isHovered ? (
<FaFolderOpen className="w-6 h-6 text-blue-400" />
bookmark.password ? (
<LuFolderLock className="w-6 h-6 text-blue-400" />
) : (
<FaFolderOpen className="w-6 h-6 text-blue-400" />
)
) : (
<FaFolder className="w-6 h-6 text-blue-400" />
))
Expand Down Expand Up @@ -65,7 +70,11 @@ export function FolderBookmarkItem({
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
style={customStyles}
className={`relative flex flex-col items-center justify-center p-4 transition-all duration-300 cursor-pointer group rounded-2xl w-full h-16 md:h-[5.5rem] shadow-sm ${!bookmark.customBackground ? getFolderStyle() : 'border hover:border-blue-400/40'} transition-all ease-in-out duration-300`}
className={`relative flex flex-col items-center justify-center p-4 transition-all duration-300 cursor-pointer group rounded-2xl w-full h-16 md:h-[5.5rem] shadow-sm ${
!bookmark.customBackground
? getFolderStyle()
: 'border hover:border-blue-400/40'
} transition-all ease-in-out duration-300`}
>
{RenderStickerPattern(bookmark)}
<div className="absolute inset-0 overflow-hidden rounded-xl">
Expand Down
21 changes: 20 additions & 1 deletion src/layouts/bookmark/components/modal/add-bookmark.modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export function AddBookmarkModal({
customBackground: '',
customTextColor: '',
sticker: '',
password: '',
})

const { fileInputRef, setIconLoadError, renderIconPreview, handleImageUpload } =
Expand Down Expand Up @@ -93,7 +94,11 @@ export function AddBookmarkModal({

if (type === 'FOLDER') {
//@ts-ignore
onAdd({ ...baseBookmark, onlineId: null } as Bookmark)
onAdd({
...baseBookmark,
onlineId: null,
password: formData.password,
} as Bookmark)
} else {
let newUrl = formData.url
if (!newUrl.startsWith('http://') && !newUrl.startsWith('https://')) {
Expand Down Expand Up @@ -222,6 +227,20 @@ export function AddBookmarkModal({
'mt-2 w-full px-4 py-3 text-right rounded-lg transition-all duration-200 '
}
/>
{type === 'FOLDER' && (
<div className="relative h-[50px]">
<TextInput
type="password"
name="password"
placeholder="رمز عبور (اختیاری)"
value={formData.password ?? ''}
onChange={(v) => updateFormData('password', v)}
className={
'mt-2 w-full px-4 py-3 text-right absolute rounded-lg transition-all duration-300'
}
/>
</div>
)}
<div className="relative h-[50px]">
{type === 'BOOKMARK' && (
<TextInput
Expand Down
18 changes: 18 additions & 0 deletions src/layouts/bookmark/components/modal/edit-bookmark.modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export function EditBookmarkModal({
customTextColor: '',
sticker: '',
touched: false,
password: '',
})

const [iconSource, setIconSource] = useState<IconSourceType>('auto')
Expand Down Expand Up @@ -64,6 +65,7 @@ export function EditBookmarkModal({
customTextColor: bookmark.customTextColor || '',
sticker: bookmark.sticker || '',
touched: false,
password: bookmark.password,
})

setIconSource(bookmark.customImage ? 'upload' : 'auto')
Expand Down Expand Up @@ -101,6 +103,7 @@ export function EditBookmarkModal({
customBackground: formData.customBackground || undefined,
customTextColor: formData.customTextColor || undefined,
sticker: formData.sticker || undefined,
password: formData.password || undefined,
}

if (type === 'BOOKMARK') {
Expand Down Expand Up @@ -190,6 +193,21 @@ export function EditBookmarkModal({
}
/>

{type === 'FOLDER' && (
<div className="relative h-[50px]">
<TextInput
type="password"
name="password"
placeholder="رمز عبور (اختیاری)"
value={formData.password ?? ''}
onChange={(v) => updateFormData('password', v)}
className={
'mt-2 w-full px-4 py-3 text-right absolute rounded-lg transition-all duration-300'
}
/>
</div>
)}

<div className="relative h-[50px]">
{type === 'BOOKMARK' && (
<TextInput
Expand Down
71 changes: 71 additions & 0 deletions src/layouts/bookmark/components/modal/folder-password.modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Button } from '@/components/button/button'
import Modal from '@/components/modal'
import { TextInput } from '@/components/text-input'

interface FolderPasswordModalProps {
isOpen: boolean
onClose: () => void
onConfirm: () => void
folderPassword: string
}

export function FolderPasswordModal({
isOpen,
onClose,
onConfirm,
folderPassword,
}: FolderPasswordModalProps) {
const [password, setPassword] = useState('')
const [errorMessage, setErrorMessage] = useState('')

const invalidPassword = 'رمز عبور اشتباه است. دوباره امتحان کنید'

return (
<Modal
direction="rtl"
title="ورود به پوشه"
isOpen={isOpen}
onClose={() => {
setPassword('')
setErrorMessage('')
onClose()
}}
>
<label htmlFor="password">رمز عبور پوشه را وارد کنید</label>
<div className="flex items-end gap-2 pb-[7px]">
<TextInput
type="password"
name="password"
id="password"
placeholder="رمز عبور"
value={password}
onChange={(v) => setPassword(v)}
className={`mt-2 w-full px-4 py-3 text-right rounded-lg transition-all duration-300 ${errorMessage && 'border-error'}`}
/>
<Button
size="md"
isPrimary={true}
disabled={!password}
onClick={() => {
if (folderPassword === password) {
onConfirm()
onClose()
setPassword('')
setErrorMessage('')
} else setErrorMessage(invalidPassword)
}}
className={
'btn btn-circle !w-fit px-4 border-none shadow-none disabled:text-gray-400 rounded-xl transition-colors duration-300 ease-in-out'
}
>
تأیید و ورود
</Button>
</div>
{errorMessage && (
<p className="text-error font-semibold text-[13px] font-[Vazir]">
{errorMessage}
</p>
)}
</Modal>
)
}
1 change: 1 addition & 0 deletions src/layouts/bookmark/components/shared.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface BookmarkFormData {
customTextColor: string
sticker: string
touched?: boolean
password?: string
}

export function IconSourceSelector({
Expand Down
2 changes: 2 additions & 0 deletions src/layouts/bookmark/types/bookmark.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ export interface Bookmark {
customTextColor?: string
sticker?: string
order?: number
password?: string
}

export interface FolderPathItem {
id: string
title: string
password?: string;
}