feat: add temporary sidebar share opinion prompt
Adds a dialog with call booking and survey actions for user feedback Shows a one-time sidebar toast prompting users to share what’s useful or missing Adds localized copy for the new feedback entry points
This commit is contained in:
@@ -35,6 +35,7 @@ import { useStickyProjectHeaders } from './sidebar/hooks/useStickyProjectHeaders
|
||||
import { getGitHubPrStatusKey, usePrVisualSummaryByKeys, useGitHubPrStatusStore } from '@/stores/useGitHubPrStatusStore';
|
||||
import { ProjectEditDialog } from '@/components/layout/ProjectEditDialog';
|
||||
import { UpdateDialog } from '@/components/ui/UpdateDialog';
|
||||
import { ShareOpinionDialog } from '@/components/feedback/ShareOpinionDialog';
|
||||
import { SessionGroupSection } from './sidebar/SessionGroupSection';
|
||||
import { SidebarHeader } from './sidebar/SidebarHeader';
|
||||
import { SidebarActivitySections } from './sidebar/SidebarActivitySections';
|
||||
@@ -80,6 +81,7 @@ import { useRuntimeAPIs } from '@/hooks/useRuntimeAPIs';
|
||||
import { useGitHubAuthStore } from '@/stores/useGitHubAuthStore';
|
||||
import { subscribeOpenchamberEvents } from '@/lib/openchamberEvents';
|
||||
|
||||
const SHARE_OPINION_TOAST_STORAGE_KEY = 'openchamber.shareOpinionToast.dismissed.v2';
|
||||
const PROJECT_COLLAPSE_STORAGE_KEY = 'oc.sessions.projectCollapse';
|
||||
const GROUP_ORDER_STORAGE_KEY = 'oc.sessions.groupOrder';
|
||||
const GROUP_COLLAPSE_STORAGE_KEY = 'oc.sessions.groupCollapse';
|
||||
@@ -196,6 +198,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
const newWorktreeDialogOpen = useUIStore((state) => state.isNewWorktreeDialogOpen);
|
||||
const setNewWorktreeDialogOpen = useUIStore((state) => state.setNewWorktreeDialogOpen);
|
||||
const [updateDialogOpen, setUpdateDialogOpen] = React.useState(false);
|
||||
const [shareOpinionDialogOpen, setShareOpinionDialogOpen] = React.useState(false);
|
||||
const [openSidebarMenuKey, setOpenSidebarMenuKey] = React.useState<string | null>(null);
|
||||
const [renamingFolderId, setRenamingFolderId] = React.useState<string | null>(null);
|
||||
const [renameFolderDraft, setRenameFolderDraft] = React.useState('');
|
||||
@@ -635,6 +638,36 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
});
|
||||
}, [t, updateStore]);
|
||||
|
||||
const handleOpenShareOpinionDialog = React.useCallback(() => {
|
||||
setShareOpinionDialogOpen(true);
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (window.localStorage.getItem(SHARE_OPINION_TOAST_STORAGE_KEY) === 'true') {
|
||||
return;
|
||||
}
|
||||
window.localStorage.setItem(SHARE_OPINION_TOAST_STORAGE_KEY, 'true');
|
||||
} catch {
|
||||
// If storage is unavailable, still show once for this sidebar mount.
|
||||
}
|
||||
const timeoutId = window.setTimeout(() => {
|
||||
toast.info(t('shareOpinion.toast.title'), {
|
||||
description: t('shareOpinion.toast.description'),
|
||||
action: {
|
||||
label: t('shareOpinion.actions.shareOpinion'),
|
||||
onClick: () => setShareOpinionDialogOpen(true),
|
||||
},
|
||||
duration: 12_000,
|
||||
});
|
||||
}, 1_000);
|
||||
|
||||
return () => window.clearTimeout(timeoutId);
|
||||
}, [t]);
|
||||
|
||||
const handleOpenSettings = React.useCallback(() => {
|
||||
if (mobileVariant) {
|
||||
setSessionSwitcherOpen(false);
|
||||
@@ -1619,10 +1652,16 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
|
||||
onOpenShortcuts={toggleHelpDialog}
|
||||
onOpenAbout={() => setAboutDialogOpen(true)}
|
||||
onOpenUpdate={handleOpenUpdateDialog}
|
||||
onOpenShareOpinion={handleOpenShareOpinionDialog}
|
||||
showRuntimeButtons={!isVSCode}
|
||||
showUpdateButton={showSidebarUpdateButton}
|
||||
/>
|
||||
|
||||
<ShareOpinionDialog
|
||||
open={shareOpinionDialogOpen}
|
||||
onOpenChange={setShareOpinionDialogOpen}
|
||||
/>
|
||||
|
||||
<UpdateDialog
|
||||
open={updateDialogOpen}
|
||||
onOpenChange={setUpdateDialogOpen}
|
||||
|
||||
@@ -9,6 +9,7 @@ type Props = {
|
||||
onOpenShortcuts: () => void;
|
||||
onOpenAbout: () => void;
|
||||
onOpenUpdate: () => void;
|
||||
onOpenShareOpinion: () => void;
|
||||
showRuntimeButtons?: boolean;
|
||||
showUpdateButton?: boolean;
|
||||
};
|
||||
@@ -20,6 +21,7 @@ export function SidebarFooter({
|
||||
onOpenShortcuts,
|
||||
onOpenAbout,
|
||||
onOpenUpdate,
|
||||
onOpenShareOpinion,
|
||||
showRuntimeButtons = true,
|
||||
showUpdateButton = true,
|
||||
}: Props): React.ReactNode {
|
||||
@@ -65,7 +67,17 @@ export function SidebarFooter({
|
||||
>
|
||||
{t('sessions.sidebar.footer.actions.update')}
|
||||
</Button>
|
||||
) : null}
|
||||
) : (
|
||||
<Button
|
||||
type="button"
|
||||
variant="default"
|
||||
size="xs"
|
||||
className="ml-auto border-[var(--primary-base)]/15 bg-[var(--primary-base)]/5 text-[var(--primary-base)]/60 hover:bg-[var(--primary-base)]/10 hover:text-[var(--primary-base)]/75"
|
||||
onClick={onOpenShareOpinion}
|
||||
>
|
||||
{t('sessions.sidebar.footer.actions.shareOpinion')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user