# 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:" 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 */}

Customize Icon

{/* Current Icon Preview */}
{/* Change/Reset Buttons */}
{formData.customIcon && ( )}
``` #### 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 ; } // Predefined icon if (icon.startsWith('icon:')) { const iconId = icon.replace('icon:', ''); const IconComponent = predefinedIconMap[iconId]; return IconComponent ? : ; } // Emoji return {icon}; }; ``` #### 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:" 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** |