Compare commits

...

1 Commits

Author SHA1 Message Date
google-labs-jules[bot]
9e91e45c70 fix: Ensure showTrayIcon is initialized from backend settings in App.tsx
The `showTrayIcon` user preference was not being explicitly passed from the
`appReady()` command's results to the `settingsStore.initSettings()` call
within `App.tsx`. This could lead to the frontend store not correctly
reflecting the persisted value of `showTrayIcon` at startup.

This commit updates `App.tsx` to include `showTrayIcon` in the
settings object passed to `initSettings`, ensuring its value is derived
from the backend configuration.
2025-06-16 14:32:39 +00:00
5 changed files with 120 additions and 26 deletions

View File

@ -195,6 +195,7 @@ function App() {
isHistoryPanelVisibleOnly: settings.isHistoryPanelVisibleOnly?.valueBool, isHistoryPanelVisibleOnly: settings.isHistoryPanelVisibleOnly?.valueBool,
isSavedClipsPanelVisibleOnly: settings.isSavedClipsPanelVisibleOnly?.valueBool, isSavedClipsPanelVisibleOnly: settings.isSavedClipsPanelVisibleOnly?.valueBool,
isSimplifiedLayout: settings.isSimplifiedLayout?.valueBool ?? true, isSimplifiedLayout: settings.isSimplifiedLayout?.valueBool ?? true,
showTrayIcon: settings.showTrayIcon?.valueBool ?? true, // Added line
isAppReady: true, isAppReady: true,
}) })
settingsStore.initConstants({ settingsStore.initConstants({

View File

@ -93,6 +93,8 @@ export default function UserPreferences() {
setIsSavedClipsPanelVisibleOnly, setIsSavedClipsPanelVisibleOnly,
isSimplifiedLayout, isSimplifiedLayout,
setIsSimplifiedLayout, setIsSimplifiedLayout,
showTrayIcon,
setShowTrayIcon,
} = useAtomValue(settingsStoreAtom) } = useAtomValue(settingsStoreAtom)
const { setFontSize, fontSize, setIsSwapPanels, isSwapPanels, returnRoute, isMacOSX } = const { setFontSize, fontSize, setIsSwapPanels, isSwapPanels, returnRoute, isMacOSX } =
@ -117,6 +119,27 @@ export default function UserPreferences() {
}) })
}, []) }, [])
// macOS-specific logic for showTrayIcon
useEffect(() => {
if (isMacOSX && !showTrayIcon) {
// If tray icon is hidden on macOS, force dock icon to be visible
// and main window to show on restart.
if (isHideMacOSDockIcon) {
setIsHideMacOSDockIcon(false)
}
if (isKeepMainWindowClosedOnRestartEnabled) {
setIsKeepMainWindowClosedOnRestartEnabled(false)
}
}
}, [
isMacOSX,
showTrayIcon,
isHideMacOSDockIcon,
setIsHideMacOSDockIcon,
isKeepMainWindowClosedOnRestartEnabled,
setIsKeepMainWindowClosedOnRestartEnabled,
])
const isDark = themeDark() const isDark = themeDark()
const [mainAppHotkey, setMainAppHotkey] = useState('') const [mainAppHotkey, setMainAppHotkey] = useState('')
@ -267,10 +290,38 @@ export default function UserPreferences() {
</Card> </Card>
</Box> </Box>
<Box className="animate-in fade-in max-w-xl mt-4">
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-1">
<CardTitle className="animate-in fade-in text-md font-medium w-full">
{t('Show System Tray Icon', { ns: 'settings2' })} {/* TODO: Add translation key */}
</CardTitle>
<Switch
checked={showTrayIcon}
className="ml-auto"
onCheckedChange={() => {
setShowTrayIcon(!showTrayIcon)
}}
/>
</CardHeader>
<CardContent>
<Text className="text-sm text-muted-foreground">
{t(
'Show the application icon in the system tray or menu bar. If disabled on macOS, the Dock icon will always be visible and the app window will show on startup.',
{ ns: 'settings2' } // TODO: Add translation key
)}
</Text>
<Text className="text-xs text-muted-foreground mt-2">
({t('app_restart_required_tray', { ns: 'settings2' })}) {/* Placeholder for translation */}
</Text>
</CardContent>
</Card>
</Box>
<Box className="animate-in fade-in max-w-xl mt-4"> <Box className="animate-in fade-in max-w-xl mt-4">
<Card <Card
className={`${ className={`${
!isKeepMainWindowClosedOnRestartEnabled (!isKeepMainWindowClosedOnRestartEnabled || (isMacOSX && !showTrayIcon))
? 'opacity-80 bg-gray-100 dark:bg-gray-900/80' ? 'opacity-80 bg-gray-100 dark:bg-gray-900/80'
: '' : ''
}`} }`}
@ -283,6 +334,7 @@ export default function UserPreferences() {
</CardTitle> </CardTitle>
<Switch <Switch
checked={isKeepMainWindowClosedOnRestartEnabled} checked={isKeepMainWindowClosedOnRestartEnabled}
disabled={isMacOSX && !showTrayIcon}
className="ml-auto" className="ml-auto"
onCheckedChange={() => { onCheckedChange={() => {
setIsKeepMainWindowClosedOnRestartEnabled( setIsKeepMainWindowClosedOnRestartEnabled(
@ -297,6 +349,11 @@ export default function UserPreferences() {
'Keep the main application window hidden when the app restarts. You can reopen it using the menu bar or taskbar menu, or using global hotkeys.', 'Keep the main application window hidden when the app restarts. You can reopen it using the menu bar or taskbar menu, or using global hotkeys.',
{ ns: 'settings2' } { ns: 'settings2' }
)} )}
{isMacOSX && !showTrayIcon && (
<Text className="text-xs text-amber-700 dark:text-amber-300 mt-1">
({t('Disabled because "Show System Tray Icon" is off.', { ns: 'settings2' })}) {/* TODO: Add translation key */}
</Text>
)}
</Text> </Text>
</CardContent> </CardContent>
</Card> </Card>
@ -923,7 +980,7 @@ export default function UserPreferences() {
<Box className="animate-in fade-in max-w-xl mt-4"> <Box className="animate-in fade-in max-w-xl mt-4">
<Card <Card
className={`${ className={`${
!isHideMacOSDockIcon && (!isHideMacOSDockIcon || (isMacOSX && !showTrayIcon)) &&
'opacity-80 bg-gray-100 dark:bg-gray-900/80' 'opacity-80 bg-gray-100 dark:bg-gray-900/80'
}`} }`}
> >
@ -940,6 +997,7 @@ export default function UserPreferences() {
</CardTitle> </CardTitle>
<Switch <Switch
checked={isHideMacOSDockIcon} checked={isHideMacOSDockIcon}
disabled={isMacOSX && !showTrayIcon}
className="ml-auto" className="ml-auto"
onCheckedChange={() => { onCheckedChange={() => {
setIsHideMacOSDockIcon(!isHideMacOSDockIcon) setIsHideMacOSDockIcon(!isHideMacOSDockIcon)
@ -952,6 +1010,11 @@ export default function UserPreferences() {
'Remove PasteBar app icon from the macOS Dock while keeping the app running in the background. The app remains accessible via the menu bar icon. Requires an app restart to take effect.', 'Remove PasteBar app icon from the macOS Dock while keeping the app running in the background. The app remains accessible via the menu bar icon. Requires an app restart to take effect.',
{ ns: 'settings2' } { ns: 'settings2' }
)} )}
{isMacOSX && !showTrayIcon && (
<Text className="text-xs text-amber-700 dark:text-amber-300 mt-1">
({t('Disabled because "Show System Tray Icon" is off.', { ns: 'settings2' })}) {/* TODO: Add translation key */}
</Text>
)}
</Text> </Text>
</CardContent> </CardContent>
</Card> </Card>

View File

@ -96,6 +96,7 @@ type Settings = {
isHistoryPanelVisibleOnly: boolean isHistoryPanelVisibleOnly: boolean
isSavedClipsPanelVisibleOnly: boolean isSavedClipsPanelVisibleOnly: boolean
isSimplifiedLayout: boolean isSimplifiedLayout: boolean
showTrayIcon: boolean
} }
type Constants = { type Constants = {
@ -178,6 +179,7 @@ export interface SettingsStoreState {
setIsSavedClipsPanelVisibleOnly: (isVisible: boolean) => void setIsSavedClipsPanelVisibleOnly: (isVisible: boolean) => void
setShowBothPanels: (isVisible: boolean) => void setShowBothPanels: (isVisible: boolean) => void
setIsSimplifiedLayout: (isEnabled: boolean) => void setIsSimplifiedLayout: (isEnabled: boolean) => void
setShowTrayIcon: (showTrayIcon: boolean) => void
hashPassword: (pass: string) => Promise<string> hashPassword: (pass: string) => Promise<string>
isNotTourCompletedOrSkipped: (tourName: string) => boolean isNotTourCompletedOrSkipped: (tourName: string) => boolean
verifyPassword: (pass: string, hash: string) => Promise<boolean> verifyPassword: (pass: string, hash: string) => Promise<boolean>
@ -269,6 +271,7 @@ const initialState: SettingsStoreState & Settings = {
isHistoryPanelVisibleOnly: false, isHistoryPanelVisibleOnly: false,
isSavedClipsPanelVisibleOnly: false, isSavedClipsPanelVisibleOnly: false,
isSimplifiedLayout: true, isSimplifiedLayout: true,
showTrayIcon: true,
CONST: { CONST: {
APP_DETECT_LANGUAGES_SUPPORTED: [], APP_DETECT_LANGUAGES_SUPPORTED: [],
}, },
@ -333,6 +336,7 @@ const initialState: SettingsStoreState & Settings = {
setIsSavedClipsPanelVisibleOnly: () => {}, setIsSavedClipsPanelVisibleOnly: () => {},
setShowBothPanels: () => {}, setShowBothPanels: () => {},
setIsSimplifiedLayout: () => {}, setIsSimplifiedLayout: () => {},
setShowTrayIcon: () => {},
initConstants: () => {}, initConstants: () => {},
setAppDataDir: () => {}, // Keep if used for other general app data setAppDataDir: () => {}, // Keep if used for other general app data
setCustomDbPath: () => {}, setCustomDbPath: () => {},
@ -698,6 +702,10 @@ export const settingsStore = createStore<SettingsStoreState & Settings>()((set,
setIsSimplifiedLayout: async (isEnabled: boolean) => { setIsSimplifiedLayout: async (isEnabled: boolean) => {
return get().updateSetting('isSimplifiedLayout', isEnabled) return get().updateSetting('isSimplifiedLayout', isEnabled)
}, },
setShowTrayIcon: async (showTrayIcon: boolean) => {
// The backend will handle tray visibility on next startup based on this persisted setting.
return get().updateSetting('showTrayIcon', showTrayIcon)
},
isNotTourCompletedOrSkipped: (tourName: string) => { isNotTourCompletedOrSkipped: (tourName: string) => {
const { appToursCompletedList, appToursSkippedList } = get() const { appToursCompletedList, appToursSkippedList } = get()
return ( return (

View File

@ -75,7 +75,9 @@ use tauri::ClipboardManager;
use tauri::Manager; use tauri::Manager;
use tauri::SystemTray; use tauri::SystemTray;
use tauri::SystemTrayEvent; use tauri::SystemTrayEvent;
use tauri::SystemTrayMenu; // Added for empty menu
// use tauri_plugin_positioner::{Position, WindowExt}; // use tauri_plugin_positioner::{Position, WindowExt};
use crate::services::user_settings_service;
use fns::debounce; use fns::debounce;
use inputbot::KeybdKey::*; use inputbot::KeybdKey::*;
@ -1079,19 +1081,30 @@ async fn main() {
} }
} }
match menu::build_tray_menu( // Determine if the tray icon should be shown
db_items_state_local, let show_tray_icon_setting = user_settings_service::get_setting("showTrayIcon");
db_recent_history_items_state, let mut show_tray_icon_bool = true; // Default to true if not found or not a bool
app_settings, if let Some(value) = show_tray_icon_setting {
) { if let serde_yaml::Value::Bool(b) = value {
Ok(tray_menu) => { show_tray_icon_bool = b;
let menu = SystemTray::new().with_id(tray_id).with_menu(tray_menu);
menu.build(app)?;
} }
Err(error_msg) => { }
debug_output(|| {
println!("Failed to build tray menu: {}", error_msg); if show_tray_icon_bool {
}); match menu::build_tray_menu(
db_items_state_local,
db_recent_history_items_state,
app_settings,
) {
Ok(tray_menu) => {
let menu = SystemTray::new().with_id(tray_id).with_menu(tray_menu);
menu.build(app)?;
}
Err(error_msg) => {
debug_output(|| {
println!("Failed to build tray menu: {}", error_msg);
});
}
} }
} }

View File

@ -16,23 +16,32 @@ pub struct UserConfig {
pub fn load_user_config() -> UserConfig { pub fn load_user_config() -> UserConfig {
let path = get_config_file_path(); let path = get_config_file_path();
if !path.exists() { let mut cfg = if !path.exists() {
return UserConfig::default(); UserConfig::default()
} } else {
match std::fs::read_to_string(&path) {
match std::fs::read_to_string(&path) { Ok(contents) => match serde_yaml::from_str::<UserConfig>(&contents) {
Ok(contents) => match serde_yaml::from_str::<UserConfig>(&contents) { Ok(parsed_cfg) => parsed_cfg,
Ok(cfg) => cfg, Err(e) => {
eprintln!("Error parsing user config YAML: {:#}", e);
UserConfig::default()
}
},
Err(e) => { Err(e) => {
eprintln!("Error parsing user config YAML: {:#}", e); eprintln!("Error reading user config file: {:#}", e);
UserConfig::default() UserConfig::default()
} }
},
Err(e) => {
eprintln!("Error reading user config file: {:#}", e);
UserConfig::default()
} }
};
// Ensure "showTrayIcon" is present
if !cfg.data.contains_key("showTrayIcon") {
cfg.data.insert(
"showTrayIcon".to_string(),
serde_yaml::Value::Bool(true),
);
} }
cfg
} }
/// Save the `UserConfig` back to `pastebar_settings.yaml`. /// Save the `UserConfig` back to `pastebar_settings.yaml`.