-
Notifications
You must be signed in to change notification settings - Fork 113
fix(category): preserve manual category with lock during sync #49
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,5 @@ | ||||||||||
| import React, { useState, useEffect } from 'react'; | ||||||||||
| import { Save, X, Plus } from 'lucide-react'; | ||||||||||
| import { Save, X, Plus, Lock, Unlock } from 'lucide-react'; | ||||||||||
| import { Modal } from './Modal'; | ||||||||||
| import { Repository } from '../types'; | ||||||||||
| import { useAppStore, getAllCategories } from '../store/useAppStore'; | ||||||||||
|
|
@@ -20,7 +20,8 @@ export const RepositoryEditModal: React.FC<RepositoryEditModalProps> = ({ | |||||||||
| const [formData, setFormData] = useState({ | ||||||||||
| description: '', | ||||||||||
| tags: [] as string[], | ||||||||||
| category: '' | ||||||||||
| category: '', | ||||||||||
| categoryLocked: false | ||||||||||
| }); | ||||||||||
| const [newTag, setNewTag] = useState(''); | ||||||||||
|
|
||||||||||
|
|
@@ -74,21 +75,27 @@ export const RepositoryEditModal: React.FC<RepositoryEditModalProps> = ({ | |||||||||
| if (repository && isOpen) { | ||||||||||
| const currentCategory = getCurrentCategory(repository); | ||||||||||
| setFormData({ | ||||||||||
| description: repository.custom_description || repository.description || '', | ||||||||||
| description: repository.custom_description || repository.ai_summary || repository.description || '', | ||||||||||
| tags: repository.custom_tags || repository.ai_tags || repository.topics || [], | ||||||||||
| category: currentCategory | ||||||||||
| category: currentCategory, | ||||||||||
| categoryLocked: !!repository.category_locked | ||||||||||
| }); | ||||||||||
| } | ||||||||||
| }, [repository, isOpen]); | ||||||||||
|
|
||||||||||
| const handleSave = () => { | ||||||||||
| if (!repository) return; | ||||||||||
|
|
||||||||||
| const originalCategory = getCurrentCategory(repository); | ||||||||||
| const categoryChanged = formData.category !== originalCategory; | ||||||||||
|
|
||||||||||
| const updatedRepo = { | ||||||||||
| ...repository, | ||||||||||
| custom_description: formData.description !== repository.description ? formData.description : undefined, | ||||||||||
| custom_tags: formData.tags.length > 0 ? formData.tags : undefined, | ||||||||||
| custom_category: formData.category ? formData.category : undefined, | ||||||||||
| // 手动修改分类时,默认自动锁定;用户仍可手动取消 | ||||||||||
| category_locked: categoryChanged ? true : formData.categoryLocked, | ||||||||||
|
Comment on lines
+97
to
+98
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check if category_locked is consulted before AI analysis overwrites fields
rg -n -A5 "analyzeRepository" --type ts | head -50Repository: AmintaCCCP/GithubStarsManager Length of output: 2256 🏁 Script executed: #!/bin/bash
# Find all places that set ai_summary, ai_tags to verify they respect the lock
rg -n "ai_summary.*=" --type tsRepository: AmintaCCCP/GithubStarsManager Length of output: 138 🏁 Script executed: cat -n src/components/RepositoryList.tsx | sed -n '176,210p'Repository: AmintaCCCP/GithubStarsManager Length of output: 1407 🏁 Script executed: # Also check if category_locked is referenced anywhere in RepositoryList.tsx
rg -n "category_locked" src/components/RepositoryList.tsxRepository: AmintaCCCP/GithubStarsManager Length of output: 55 🏁 Script executed: # Search for all references to category_locked across the codebase
rg -n "category_locked" --type ts --type tsxRepository: AmintaCCCP/GithubStarsManager Length of output: 100 🏁 Script executed: # Check the Repository type definition to understand category_locked field
rg -n "category_locked" --type ts --type tsx -B 2 -A 2Repository: AmintaCCCP/GithubStarsManager Length of output: 100 🏁 Script executed: # Search for all references to category_locked across the codebase
rg -n "category_locked"Repository: AmintaCCCP/GithubStarsManager Length of output: 440 🏁 Script executed: # Check the Repository type definition
fd ".*Repository.*" -e ts -e tsx | head -20Repository: AmintaCCCP/GithubStarsManager Length of output: 179 🏁 Script executed: # Check the comment/context around category_locked in types
cat -n src/types/index.ts | sed -n '25,35p'Repository: AmintaCCCP/GithubStarsManager Length of output: 422 🏁 Script executed: # Check Header.tsx to see how category_locked is used there
cat -n src/components/Header.tsx | sed -n '50,60p'Repository: AmintaCCCP/GithubStarsManager Length of output: 573 🏁 Script executed: # Search for where category_locked is actually checked (used in conditionals)
rg -n "category_locked" -B 2 -A 2 --context 3Repository: AmintaCCCP/GithubStarsManager Length of output: 2117 🏁 Script executed: # Check if there's any sync logic that respects category_locked
rg -n "sync" -A 5 -B 5 src/components/RepositoryList.tsx | head -60Repository: AmintaCCCP/GithubStarsManager Length of output: 1394 🏁 Script executed: # Check the entire RepositoryList.tsx to see the broader context of the batch AI analysis
cat -n src/components/RepositoryList.tsx | sed -n '1,50p'Repository: AmintaCCCP/GithubStarsManager Length of output: 1949 🏁 Script executed: # Check if category_locked is checked anywhere in sync operations
rg -n "sync" --context 5 src/components/Header.tsx | head -80Repository: AmintaCCCP/GithubStarsManager Length of output: 949 🏁 Script executed: # Look for where the sync actually happens and if it checks category_locked
rg -n "updateRepository\|setRepositories" src/components/Header.tsx -B 3 -A 3 | head -50Repository: AmintaCCCP/GithubStarsManager Length of output: 55 🏁 Script executed: # Look at the full handleSync function in Header.tsx
cat -n src/components/Header.tsx | sed -n '25,100p'Repository: AmintaCCCP/GithubStarsManager Length of output: 3166 🏁 Script executed: # Check if category_locked is checked anywhere during repository updates or sync
rg -n "category_locked" -B 5 -A 5 src/components/Header.tsxRepository: AmintaCCCP/GithubStarsManager Length of output: 529 Add The 🤖 Prompt for AI Agents |
||||||||||
| last_edited: new Date().toISOString() | ||||||||||
| }; | ||||||||||
|
|
||||||||||
|
|
@@ -100,7 +107,8 @@ export const RepositoryEditModal: React.FC<RepositoryEditModalProps> = ({ | |||||||||
| setFormData({ | ||||||||||
| description: '', | ||||||||||
| tags: [], | ||||||||||
| category: '' | ||||||||||
| category: '', | ||||||||||
| categoryLocked: false | ||||||||||
| }); | ||||||||||
| setNewTag(''); | ||||||||||
| onClose(); | ||||||||||
|
|
@@ -202,6 +210,36 @@ export const RepositoryEditModal: React.FC<RepositoryEditModalProps> = ({ | |||||||||
| {t('当前分类:', 'Current category:')} {formData.category} | ||||||||||
| </p> | ||||||||||
| )} | ||||||||||
|
|
||||||||||
| <div className="mt-3 flex items-center justify-between rounded-lg border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 px-3 py-2"> | ||||||||||
| <div className="flex items-center space-x-2"> | ||||||||||
| {formData.categoryLocked ? ( | ||||||||||
| <Lock className="w-4 h-4 text-amber-600 dark:text-amber-400" /> | ||||||||||
| ) : ( | ||||||||||
| <Unlock className="w-4 h-4 text-gray-500 dark:text-gray-400" /> | ||||||||||
| )} | ||||||||||
| <span className="text-sm text-gray-700 dark:text-gray-300"> | ||||||||||
| {t('分类锁定', 'Category Lock')} | ||||||||||
| </span> | ||||||||||
| </div> | ||||||||||
| <label | ||||||||||
| className="inline-flex items-center cursor-pointer" | ||||||||||
| title={t( | ||||||||||
| '开启后,同步时不会自动修改该仓库分类。手动修改分类会自动开启此开关。', | ||||||||||
| 'When enabled, sync will not auto-change this repository category. Manually changing category will enable this lock by default.' | ||||||||||
| )} | ||||||||||
| > | ||||||||||
| <input | ||||||||||
| type="checkbox" | ||||||||||
| checked={formData.categoryLocked} | ||||||||||
| onChange={(e) => setFormData(prev => ({ ...prev, categoryLocked: e.target.checked }))} | ||||||||||
| className="sr-only peer" | ||||||||||
|
Comment on lines
+225
to
+236
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Give the lock switch an accessible name. The visible “Category Lock” text is outside the 🤖 Prompt for AI Agents |
||||||||||
| /> | ||||||||||
| <div className="relative w-10 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-blue-500 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:bg-blue-600"> | ||||||||||
| <div className="absolute top-[2px] left-[2px] bg-white border-gray-300 border rounded-full h-5 w-5 transition-transform peer-checked:translate-x-full"></div> | ||||||||||
| </div> | ||||||||||
|
Comment on lines
+238
to
+240
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Toggle knob won't animate — Tailwind's ♻️ Fix using `after:` pseudo-element pattern- <div className="relative w-10 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-blue-500 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:bg-blue-600">
- <div className="absolute top-[2px] left-[2px] bg-white border-gray-300 border rounded-full h-5 w-5 transition-transform peer-checked:translate-x-full"></div>
- </div>
+ <div className="relative w-10 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-blue-500 dark:peer-focus:ring-blue-800 rounded-full dark:bg-gray-700 peer-checked:bg-blue-600 after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-transform peer-checked:after:translate-x-full"></div>This uses the 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||
| </label> | ||||||||||
| </div> | ||||||||||
| </div> | ||||||||||
|
|
||||||||||
| {/* Tags */} | ||||||||||
|
|
||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The save path can silently override the switch state.
When the category changes, Line 98 always persists
category_locked: true, even if the user turned the toggle off in the same dialog. That leaves the control showing one state and saving another. Either force the toggle on/disable it as soon as the category changes, or makehandleSave()respect the current toggle value.🤖 Prompt for AI Agents