Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
|
|
@ -0,0 +1,391 @@
|
|||
# Collection Icon Customization Plan
|
||||
|
||||
## Overview
|
||||
|
||||
Add the ability to customize collection (folder) icons with emojis or predefined icons. This feature enhances the user experience by allowing visual differentiation between collections.
|
||||
|
||||
## Requirements
|
||||
|
||||
1. **Customization Options**:
|
||||
- Custom emoji (e.g., 📁, 🎵, 📷, 💼, 🏠)
|
||||
- Predefined cross-browser icons from a curated set
|
||||
- Default folder icon when no customization is set
|
||||
|
||||
2. **Behavior**:
|
||||
- Default to standard folder icon if no customization
|
||||
- Persist across browser sessions (stored in database)
|
||||
- Easy to change or revert to default
|
||||
- Intuitive, user-friendly UI
|
||||
|
||||
3. **Security Consideration**:
|
||||
- Icon data should be encrypted (E2EE) like collection name
|
||||
- Only emoji characters or predefined icon identifiers allowed
|
||||
|
||||
---
|
||||
|
||||
## Data Model Design
|
||||
|
||||
### New Field: `custom_icon`
|
||||
|
||||
Add a new encrypted field to the Collection model that stores icon customization data.
|
||||
|
||||
```go
|
||||
// CustomIcon stores the collection's custom icon configuration
|
||||
type CustomIcon struct {
|
||||
Type string `json:"type"` // "emoji", "icon", or "" (empty = default)
|
||||
Value string `json:"value"` // Emoji character or icon identifier
|
||||
}
|
||||
```
|
||||
|
||||
**Field Storage Options**:
|
||||
|
||||
| Option | Pros | Cons |
|
||||
|--------|------|------|
|
||||
| A) Single encrypted JSON field | Simple, one field | Requires parsing |
|
||||
| B) Two fields (type + value) | Clear structure | More columns |
|
||||
| C) Single string field | Simplest | Limited validation |
|
||||
|
||||
**Recommended: Option C** - Single `encrypted_custom_icon` field storing either:
|
||||
- Empty string `""` → Default folder icon
|
||||
- Emoji character (e.g., `"📷"`) → Display as emoji
|
||||
- Icon identifier (e.g., `"icon:briefcase"`) → Predefined icon
|
||||
|
||||
This keeps the schema simple and the client handles interpretation.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Backend (cloud/maplefile-backend)
|
||||
|
||||
#### 1.1 Database Schema Update
|
||||
|
||||
**Note:** No new migration files needed - updating existing migration `012_create_collections_by_id.up.cql` directly (assumes full database wipe).
|
||||
|
||||
Add to `collections_by_id` table:
|
||||
```sql
|
||||
encrypted_custom_icon TEXT,
|
||||
```
|
||||
|
||||
#### 1.2 Update Domain Model
|
||||
|
||||
File: `internal/domain/collection/model.go`
|
||||
|
||||
```go
|
||||
type Collection struct {
|
||||
// ... existing fields ...
|
||||
|
||||
// EncryptedCustomIcon stores the custom icon for this collection.
|
||||
// Empty string means use default folder icon.
|
||||
// Contains either an emoji character or "icon:<identifier>" for predefined icons.
|
||||
// Encrypted with the collection key for E2EE.
|
||||
EncryptedCustomIcon string `bson:"encrypted_custom_icon" json:"encrypted_custom_icon"`
|
||||
}
|
||||
```
|
||||
|
||||
Also add to `CollectionSyncItem` for sync operations:
|
||||
```go
|
||||
type CollectionSyncItem struct {
|
||||
// ... existing fields ...
|
||||
EncryptedCustomIcon string `json:"encrypted_custom_icon,omitempty" bson:"encrypted_custom_icon,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** The sync query in `collectionsync.go:getCollectionSyncItem()` fetches minimal data from `collections_by_id`. This query will need to include `encrypted_custom_icon` so clients can display the correct icon during sync.
|
||||
|
||||
#### 1.3 Update Repository Layer
|
||||
|
||||
Files to modify:
|
||||
- `internal/repo/collection/create.go` - Include new field in INSERT
|
||||
- `internal/repo/collection/update.go` - Include new field in UPDATE
|
||||
- `internal/repo/collection/get.go` - Include new field in SELECT
|
||||
- `internal/repo/collection/sync.go` - Include new field in sync queries
|
||||
|
||||
#### 1.4 Update HTTP Handlers (if needed)
|
||||
|
||||
The existing create/update endpoints should automatically handle the new field since they accept the full Collection struct.
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: Frontend (web/maplefile-frontend)
|
||||
|
||||
#### 2.1 Create Icon Picker Component
|
||||
|
||||
New file: `src/components/IconPicker/IconPicker.jsx`
|
||||
|
||||
Features:
|
||||
- Emoji picker tab with common categories (objects, activities, symbols)
|
||||
- Predefined icons tab (Heroicons subset)
|
||||
- "Default" option to revert to folder icon
|
||||
- Search/filter functionality
|
||||
- Recently used icons
|
||||
|
||||
```jsx
|
||||
// Example structure
|
||||
const IconPicker = ({ value, onChange, onClose }) => {
|
||||
const [activeTab, setActiveTab] = useState('emoji'); // 'emoji' | 'icons'
|
||||
|
||||
const predefinedIcons = [
|
||||
{ id: 'briefcase', icon: BriefcaseIcon, label: 'Work' },
|
||||
{ id: 'photo', icon: PhotoIcon, label: 'Photos' },
|
||||
{ id: 'music', icon: MusicalNoteIcon, label: 'Music' },
|
||||
{ id: 'document', icon: DocumentIcon, label: 'Documents' },
|
||||
{ id: 'archive', icon: ArchiveBoxIcon, label: 'Archive' },
|
||||
// ... more icons
|
||||
];
|
||||
|
||||
const popularEmojis = ['📁', '📷', '🎵', '💼', '🏠', '❤️', '⭐', '🎮', '📚', '🎨'];
|
||||
|
||||
return (
|
||||
// ... picker UI
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
#### 2.2 Update CollectionEdit Page
|
||||
|
||||
File: `src/pages/User/FileManager/Collections/CollectionEdit.jsx`
|
||||
|
||||
Add icon customization section:
|
||||
|
||||
```jsx
|
||||
{/* Icon Customization Section */}
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
||||
<h2 className="text-lg font-semibold text-gray-900 mb-4 flex items-center">
|
||||
<SparklesIcon className="h-5 w-5 mr-2 text-gray-500" />
|
||||
Customize Icon
|
||||
</h2>
|
||||
|
||||
<div className="flex items-center space-x-4">
|
||||
{/* Current Icon Preview */}
|
||||
<div className="flex items-center justify-center h-16 w-16 bg-gray-100 rounded-xl border-2 border-dashed border-gray-300">
|
||||
<CollectionIcon icon={formData.customIcon} size="lg" />
|
||||
</div>
|
||||
|
||||
{/* Change/Reset Buttons */}
|
||||
<div className="space-y-2">
|
||||
<button onClick={() => setShowIconPicker(true)} className="...">
|
||||
Change Icon
|
||||
</button>
|
||||
{formData.customIcon && (
|
||||
<button onClick={() => setFormData({...formData, customIcon: ''})} className="...">
|
||||
Reset to Default
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### 2.3 Create CollectionIcon Component
|
||||
|
||||
New file: `src/components/CollectionIcon/CollectionIcon.jsx`
|
||||
|
||||
Renders the appropriate icon based on the customIcon value:
|
||||
|
||||
```jsx
|
||||
const CollectionIcon = ({ icon, collectionType = 'folder', size = 'md', className = '' }) => {
|
||||
const sizes = {
|
||||
sm: 'h-4 w-4',
|
||||
md: 'h-6 w-6',
|
||||
lg: 'h-10 w-10',
|
||||
};
|
||||
|
||||
// Default folder/album icon
|
||||
if (!icon || icon === '') {
|
||||
const Icon = collectionType === 'album' ? PhotoIcon : FolderIcon;
|
||||
return <Icon className={`${sizes[size]} ${className}`} />;
|
||||
}
|
||||
|
||||
// Predefined icon
|
||||
if (icon.startsWith('icon:')) {
|
||||
const iconId = icon.replace('icon:', '');
|
||||
const IconComponent = predefinedIconMap[iconId];
|
||||
return IconComponent ? <IconComponent className={`${sizes[size]} ${className}`} /> : <FolderIcon className={`${sizes[size]} ${className}`} />;
|
||||
}
|
||||
|
||||
// Emoji
|
||||
return <span className={`${emojiSizes[size]} ${className}`}>{icon}</span>;
|
||||
};
|
||||
```
|
||||
|
||||
#### 2.4 Update Collection List/Grid Views
|
||||
|
||||
Update anywhere collections are displayed to use the new `CollectionIcon` component:
|
||||
- `FileManagerIndex.jsx`
|
||||
- `CollectionDetails.jsx`
|
||||
- Sidebar navigation (if applicable)
|
||||
|
||||
#### 2.5 Update Encryption/Decryption
|
||||
|
||||
Update the collection encryption service to handle the new field:
|
||||
- Encrypt `customIcon` when saving
|
||||
- Decrypt `encrypted_custom_icon` when loading
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: Native Desktop (native/desktop/maplefile)
|
||||
|
||||
#### 3.1 Update Domain Model
|
||||
|
||||
File: `internal/domain/collection/model.go`
|
||||
|
||||
```go
|
||||
type Collection struct {
|
||||
// ... existing fields ...
|
||||
|
||||
// CustomIcon stores the decrypted custom icon for this collection.
|
||||
// Empty string means use default folder icon.
|
||||
// Contains either an emoji character or "icon:<identifier>" for predefined icons.
|
||||
CustomIcon string `json:"custom_icon,omitempty"`
|
||||
|
||||
// EncryptedCustomIcon is the encrypted version from cloud
|
||||
EncryptedCustomIcon string `json:"encrypted_custom_icon,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2 Update Sync Service
|
||||
|
||||
Ensure the sync service handles the new field when syncing collections from the cloud.
|
||||
|
||||
#### 3.3 Update Frontend (Wails)
|
||||
|
||||
The desktop frontend uses the same React patterns, so the IconPicker and CollectionIcon components can be shared or adapted.
|
||||
|
||||
---
|
||||
|
||||
## Predefined Icon Set
|
||||
|
||||
A curated set of cross-browser compatible icons:
|
||||
|
||||
| ID | Icon | Use Case |
|
||||
|----|------|----------|
|
||||
| `briefcase` | BriefcaseIcon | Work |
|
||||
| `photo` | PhotoIcon | Photos |
|
||||
| `music` | MusicalNoteIcon | Music |
|
||||
| `video` | VideoCameraIcon | Videos |
|
||||
| `document` | DocumentTextIcon | Documents |
|
||||
| `archive` | ArchiveBoxIcon | Archive |
|
||||
| `star` | StarIcon | Favorites |
|
||||
| `heart` | HeartIcon | Personal |
|
||||
| `home` | HomeIcon | Home |
|
||||
| `academic` | AcademicCapIcon | School |
|
||||
| `code` | CodeBracketIcon | Code |
|
||||
| `cloud` | CloudIcon | Cloud |
|
||||
| `lock` | LockClosedIcon | Private |
|
||||
| `gift` | GiftIcon | Gifts |
|
||||
| `calendar` | CalendarIcon | Events |
|
||||
|
||||
---
|
||||
|
||||
## Migration Strategy
|
||||
|
||||
1. **Backward Compatible**: Empty `encrypted_custom_icon` means default icon
|
||||
2. **No Data Migration Needed**: New collections get the field, old collections have NULL/empty
|
||||
3. **Clients Handle Missing Field**: Treat NULL/empty as default
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### Backend
|
||||
- [ ] Migration runs successfully
|
||||
- [ ] Create collection with custom icon
|
||||
- [ ] Update collection custom icon
|
||||
- [ ] Revert to default icon
|
||||
- [ ] Sync includes custom icon field
|
||||
- [ ] E2EE encryption/decryption works
|
||||
|
||||
### Frontend (Web)
|
||||
- [ ] Icon picker opens and closes
|
||||
- [ ] Emoji selection works
|
||||
- [ ] Predefined icon selection works
|
||||
- [ ] Reset to default works
|
||||
- [ ] Icon persists after page reload
|
||||
- [ ] Icon displays correctly in list/grid views
|
||||
- [ ] Works across different browsers
|
||||
|
||||
### Native Desktop
|
||||
- [ ] Sync downloads custom icon
|
||||
- [ ] Icon displays correctly
|
||||
- [ ] Edit icon works (if implemented)
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **E2EE**: Custom icon is encrypted with collection key
|
||||
2. **Input Validation**: Only allow valid emoji or predefined icon IDs
|
||||
3. **XSS Prevention**: Sanitize icon display (emoji rendering, no HTML)
|
||||
4. **Size Limits**: Max length for custom icon field (e.g., 50 chars)
|
||||
|
||||
---
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
1. **Custom uploaded icons** (requires more complex storage)
|
||||
2. **Icon color customization**
|
||||
3. **Icon packs/themes**
|
||||
4. **Bulk icon changes** (apply to multiple collections)
|
||||
|
||||
---
|
||||
|
||||
## Files to Modify
|
||||
|
||||
### Backend (cloud/maplefile-backend)
|
||||
|
||||
**Schema Update (modify existing migration):**
|
||||
1. `migrations/012_create_collections_by_id.up.cql` - Add `encrypted_custom_icon TEXT` column
|
||||
|
||||
**Domain Layer:**
|
||||
2. `internal/domain/collection/model.go` - Add `EncryptedCustomIcon` field to `Collection` and `CollectionSyncItem` structs
|
||||
|
||||
**Repository Layer:**
|
||||
4. `internal/repo/collection/create.go` - Add `encrypted_custom_icon` to INSERT query (line ~57-66)
|
||||
5. `internal/repo/collection/update.go` - Add `encrypted_custom_icon` to UPDATE query (line ~64-73)
|
||||
6. `internal/repo/collection/get.go` - Add `encrypted_custom_icon` to SELECT query and scan (line ~44-52, ~72-90)
|
||||
7. `internal/repo/collection/collectionsync.go` - Add field to sync queries
|
||||
|
||||
**Note:** Secondary tables (013-017) do NOT need modification - they are lookup/index tables that only store keys and minimal fields. The `encrypted_custom_icon` is stored only in `collections_by_id`.
|
||||
|
||||
### Frontend (web/maplefile-frontend)
|
||||
|
||||
**New Components:**
|
||||
1. `src/components/IconPicker/IconPicker.jsx` - Modal with emoji grid + predefined icons
|
||||
2. `src/components/CollectionIcon/CollectionIcon.jsx` - Renders appropriate icon based on value
|
||||
|
||||
**Modified Pages:**
|
||||
3. `src/pages/User/FileManager/Collections/CollectionEdit.jsx` - Add icon customization section
|
||||
4. `src/pages/User/FileManager/Collections/CollectionDetails.jsx` - Display custom icon
|
||||
5. `src/pages/User/FileManager/Collections/CollectionCreate.jsx` - Optional icon selection on create
|
||||
6. `src/pages/User/FileManager/FileManagerIndex.jsx` - Display custom icons in list/grid
|
||||
|
||||
**Services:**
|
||||
7. Collection encryption service - Handle `customIcon` field encryption/decryption
|
||||
|
||||
### Native Desktop (native/desktop/maplefile)
|
||||
|
||||
**Domain:**
|
||||
1. `internal/domain/collection/model.go` - Add `CustomIcon` and `EncryptedCustomIcon` fields
|
||||
|
||||
**Repository:**
|
||||
2. `internal/repo/collection/repository.go` - Handle new field in CRUD operations
|
||||
|
||||
**Sync:**
|
||||
3. `internal/service/sync/collection.go` - Include field in sync operations
|
||||
|
||||
**Frontend:**
|
||||
4. `frontend/src/` - Adapt IconPicker and CollectionIcon components (if not shared with web)
|
||||
|
||||
---
|
||||
|
||||
## Estimated Effort
|
||||
|
||||
| Phase | Effort |
|
||||
|-------|--------|
|
||||
| Backend (migration + model) | 2-3 hours |
|
||||
| Frontend components | 4-6 hours |
|
||||
| Frontend integration | 2-3 hours |
|
||||
| Native desktop | 2-3 hours |
|
||||
| Testing | 2-3 hours |
|
||||
| **Total** | **12-18 hours** |
|
||||
Loading…
Add table
Add a link
Reference in a new issue