mirror of
http://10.0.2.1:3031/sauer/claude-code.git
synced 2026-06-30 18:16:58 +10:00
100 lines
14 KiB
TypeScript
100 lines
14 KiB
TypeScript
|
|
import { randomUUID } from 'crypto';
|
||
|
|
import { useCallback, useRef, useState } from 'react';
|
||
|
|
import type { TranscriptShareResponse } from './TranscriptSharePrompt.js';
|
||
|
|
import type { FeedbackSurveyResponse } from './utils.js';
|
||
|
|
type SurveyState = 'closed' | 'open' | 'thanks' | 'transcript_prompt' | 'submitting' | 'submitted';
|
||
|
|
type UseSurveyStateOptions = {
|
||
|
|
hideThanksAfterMs: number;
|
||
|
|
onOpen: (appearanceId: string) => void | Promise<void>;
|
||
|
|
onSelect: (appearanceId: string, selected: FeedbackSurveyResponse) => void | Promise<void>;
|
||
|
|
shouldShowTranscriptPrompt?: (selected: FeedbackSurveyResponse) => boolean;
|
||
|
|
onTranscriptPromptShown?: (appearanceId: string, surveyResponse: FeedbackSurveyResponse) => void;
|
||
|
|
onTranscriptSelect?: (appearanceId: string, selected: TranscriptShareResponse, surveyResponse: FeedbackSurveyResponse | null) => boolean | Promise<boolean>;
|
||
|
|
};
|
||
|
|
export function useSurveyState({
|
||
|
|
hideThanksAfterMs,
|
||
|
|
onOpen,
|
||
|
|
onSelect,
|
||
|
|
shouldShowTranscriptPrompt,
|
||
|
|
onTranscriptPromptShown,
|
||
|
|
onTranscriptSelect
|
||
|
|
}: UseSurveyStateOptions): {
|
||
|
|
state: SurveyState;
|
||
|
|
lastResponse: FeedbackSurveyResponse | null;
|
||
|
|
open: () => void;
|
||
|
|
handleSelect: (selected: FeedbackSurveyResponse) => boolean;
|
||
|
|
handleTranscriptSelect: (selected: TranscriptShareResponse) => void;
|
||
|
|
} {
|
||
|
|
const [state, setState] = useState<SurveyState>('closed');
|
||
|
|
const [lastResponse, setLastResponse] = useState<FeedbackSurveyResponse | null>(null);
|
||
|
|
const appearanceId = useRef(randomUUID());
|
||
|
|
const lastResponseRef = useRef<FeedbackSurveyResponse | null>(null);
|
||
|
|
const showThanksThenClose = useCallback(() => {
|
||
|
|
setState('thanks');
|
||
|
|
setTimeout((setState_0, setLastResponse_0) => {
|
||
|
|
setState_0('closed');
|
||
|
|
setLastResponse_0(null);
|
||
|
|
}, hideThanksAfterMs, setState, setLastResponse);
|
||
|
|
}, [hideThanksAfterMs]);
|
||
|
|
const showSubmittedThenClose = useCallback(() => {
|
||
|
|
setState('submitted');
|
||
|
|
setTimeout(setState, hideThanksAfterMs, 'closed');
|
||
|
|
}, [hideThanksAfterMs]);
|
||
|
|
const open = useCallback(() => {
|
||
|
|
if (state !== 'closed') {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
setState('open');
|
||
|
|
appearanceId.current = randomUUID();
|
||
|
|
void onOpen(appearanceId.current);
|
||
|
|
}, [state, onOpen]);
|
||
|
|
const handleSelect = useCallback((selected: FeedbackSurveyResponse): boolean => {
|
||
|
|
setLastResponse(selected);
|
||
|
|
lastResponseRef.current = selected;
|
||
|
|
// Always fire the survey response event first
|
||
|
|
void onSelect(appearanceId.current, selected);
|
||
|
|
if (selected === 'dismissed') {
|
||
|
|
setState('closed');
|
||
|
|
setLastResponse(null);
|
||
|
|
} else if (shouldShowTranscriptPrompt?.(selected)) {
|
||
|
|
setState('transcript_prompt');
|
||
|
|
onTranscriptPromptShown?.(appearanceId.current, selected);
|
||
|
|
return true;
|
||
|
|
} else {
|
||
|
|
showThanksThenClose();
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}, [showThanksThenClose, onSelect, shouldShowTranscriptPrompt, onTranscriptPromptShown]);
|
||
|
|
const handleTranscriptSelect = useCallback((selected_0: TranscriptShareResponse) => {
|
||
|
|
switch (selected_0) {
|
||
|
|
case 'yes':
|
||
|
|
setState('submitting');
|
||
|
|
void (async () => {
|
||
|
|
try {
|
||
|
|
const success = await onTranscriptSelect?.(appearanceId.current, selected_0, lastResponseRef.current);
|
||
|
|
if (success) {
|
||
|
|
showSubmittedThenClose();
|
||
|
|
} else {
|
||
|
|
showThanksThenClose();
|
||
|
|
}
|
||
|
|
} catch {
|
||
|
|
showThanksThenClose();
|
||
|
|
}
|
||
|
|
})();
|
||
|
|
break;
|
||
|
|
case 'no':
|
||
|
|
case 'dont_ask_again':
|
||
|
|
void onTranscriptSelect?.(appearanceId.current, selected_0, lastResponseRef.current);
|
||
|
|
showThanksThenClose();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}, [showThanksThenClose, showSubmittedThenClose, onTranscriptSelect]);
|
||
|
|
return {
|
||
|
|
state,
|
||
|
|
lastResponse,
|
||
|
|
open,
|
||
|
|
handleSelect,
|
||
|
|
handleTranscriptSelect
|
||
|
|
};
|
||
|
|
}
|
||
|
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJyYW5kb21VVUlEIiwidXNlQ2FsbGJhY2siLCJ1c2VSZWYiLCJ1c2VTdGF0ZSIsIlRyYW5zY3JpcHRTaGFyZVJlc3BvbnNlIiwiRmVlZGJhY2tTdXJ2ZXlSZXNwb25zZSIsIlN1cnZleVN0YXRlIiwiVXNlU3VydmV5U3RhdGVPcHRpb25zIiwiaGlkZVRoYW5rc0FmdGVyTXMiLCJvbk9wZW4iLCJhcHBlYXJhbmNlSWQiLCJQcm9taXNlIiwib25TZWxlY3QiLCJzZWxlY3RlZCIsInNob3VsZFNob3dUcmFuc2NyaXB0UHJvbXB0Iiwib25UcmFuc2NyaXB0UHJvbXB0U2hvd24iLCJzdXJ2ZXlSZXNwb25zZSIsIm9uVHJhbnNjcmlwdFNlbGVjdCIsInVzZVN1cnZleVN0YXRlIiwic3RhdGUiLCJsYXN0UmVzcG9uc2UiLCJvcGVuIiwiaGFuZGxlU2VsZWN0IiwiaGFuZGxlVHJhbnNjcmlwdFNlbGVjdCIsInNldFN0YXRlIiwic2V0TGFzdFJlc3BvbnNlIiwibGFzdFJlc3BvbnNlUmVmIiwic2hvd1RoYW5rc1RoZW5DbG9zZSIsInNldFRpbWVvdXQiLCJzaG93U3VibWl0dGVkVGhlbkNsb3NlIiwiY3VycmVudCIsInN1Y2Nlc3MiXSwic291cmNlcyI6WyJ1c2VTdXJ2ZXlTdGF0ZS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgcmFuZG9tVVVJRCB9IGZyb20gJ2NyeXB0bydcbmltcG9ydCB7IHVzZUNhbGxiYWNrLCB1c2VSZWYsIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgdHlwZSB7IFRyYW5zY3JpcHRTaGFyZVJlc3BvbnNlIH0gZnJvbSAnLi9UcmFuc2NyaXB0U2hhcmVQcm9tcHQuanMnXG5pbXBvcnQgdHlwZSB7IEZlZWRiYWNrU3VydmV5UmVzcG9uc2UgfSBmcm9tICcuL3V0aWxzLmpzJ1xuXG50eXBlIFN1cnZleVN0YXRlID1cbiAgfCAnY2xvc2VkJ1xuICB8ICdvcGVuJ1xuICB8ICd0aGFua3MnXG4gIHwgJ3RyYW5zY3JpcHRfcHJvbXB0J1xuICB8ICdzdWJtaXR0aW5nJ1xuICB8ICdzdWJtaXR0ZWQnXG5cbnR5cGUgVXNlU3VydmV5U3RhdGVPcHRpb25zID0ge1xuICBoaWRlVGhhbmtzQWZ0ZXJNczogbnVtYmVyXG4gIG9uT3BlbjogKGFwcGVhcmFuY2VJZDogc3RyaW5nKSA9PiB2b2lkIHwgUHJvbWlzZTx2b2lkPlxuICBvblNlbGVjdDogKFxuICAgIGFwcGVhcmFuY2VJZDogc3RyaW5nLFxuICAgIHNlbGVjdGVkOiBGZWVkYmFja1N1cnZleVJlc3BvbnNlLFxuICApID0+IHZvaWQgfCBQcm9taXNlPHZvaWQ+XG4gIHNob3VsZFNob3dUcmFuc2NyaXB0UHJvbXB0PzogKHNlbGVjdGVkOiBGZWVkYmFja1N1cnZleVJlc3BvbnNlKSA9PiBib29sZWFuXG4gIG9uVHJhbnNjcmlwdFByb21wdFNob3duPzogKFxuICAgIGFwcGVhcmFuY2VJZDogc3RyaW5nLFxuICAgIHN1cnZleVJlc3BvbnNlOiBGZWVkYmFja1N1cnZleVJlc3BvbnNlLFxuICApID0+IHZvaWRcbiAgb25UcmFuc2NyaXB0U2VsZWN0PzogKFxuICAgIGFwcGVhcmFuY2VJZDogc3RyaW5nLFxuICAgIHNlbGVjdGVkOiBUcmFuc2NyaXB0U2hhcmVSZXNwb25zZSxcbiAgICBzdXJ2ZXlSZXNwb25zZTogRmVlZGJhY2tTdXJ2ZXlSZXNwb25zZSB8IG51bGwsXG4gICkgPT4gYm9vbGVhbiB8IFByb21pc2U8Ym9vbGVhbj5cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVzZVN1cnZleVN0YXRlKHtcbiAgaGlkZVRoYW5rc0FmdGVyTXMsXG4gIG9uT3BlbixcbiAgb25TZWxlY3QsXG4gIHNob3VsZFNob3dUcmFuc2NyaXB0UHJvbXB0LFxuICBvblRyYW5zY3JpcHRQcm9tcHRTaG93bixcbiAgb25UcmFuc2NyaXB0U2VsZWN0LFxufTogVXNlU3VydmV5U3RhdGVPcHRpb25zKToge1xuICBzdGF0ZTogU3VydmV5U3RhdGVcbiAgbGFzdFJlc3BvbnNlOiBGZWVkYmFja1N1cnZleVJlc3BvbnNlIHwgbnVsbFxuICBvcGVuOiAoKSA9PiB2b2lkXG4gIGhhbmRsZVNlbGVjdDogKHNlbGVjdGVkOiBGZWVkYmFja1N1cnZleVJlc3BvbnNlKSA9PiBib29sZWFuXG4gIGhhbmRsZVRyYW5zY3JpcHRTZWxlY3Q6IChzZWxlY3RlZDogVHJhbnNjcmlwdFNoYXJlUmVzcG9uc2UpID0+IHZvaWRcbn0ge1xuICBjb25zdCBbc3RhdGUsIHNldFN0YXRlXSA9IHVzZVN0YXRlPFN1cnZleVN0YXRlPignY2xvc2VkJylcbiAgY29uc3QgW2xhc3RSZXNwb25zZSwgc2V0TGFzdFJlc3BvbnNlXSA9XG4gICAgdXNlU3RhdGU8RmVlZGJhY2tTdXJ2ZXlSZXNwb25zZSB8IG51bGw+KG51bGwpXG4gIGNvbnN0IGFwcGVhcmFuY2VJZCA9IHVzZVJlZihyYW5kb21VVUlEKCkpXG4gIGNvbnN0IGxhc3RSZXNwb25zZVJlZiA9IHVzZVJlZjxGZWVkYmFja1N1cnZleVJlc3BvbnNlIHwgbnVsbD4obnVsbClcblxuICBjb25zdCBzaG93VGhhbmtzVGhlbkNsb3NlID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIHNldFN0YXRlKCd0aGFua3MnKVxuICAgIHNldFRpbWVvdXQoXG4gICAgICAoc2V0U3RhdGUsIHNldExhc3RSZXNwb25zZSkgPT4ge1xuICAgICAgICBzZXRTdGF0ZSgnY2xvc2VkJylcbiAgICAgICAgc2V0TGFzdFJlc3BvbnNlKG51bGwpXG4gICAgICB9LFxuICAgICAgaGlkZVRoYW5rc0FmdGVyTXMsXG4gICAgICBzZXRTdGF0ZSxcbiAgICAgIHNldExhc3RSZXNwb25zZSxcbiAgICApXG4gIH0sIFtoaWRlVGhhbmtzQWZ0ZXJNc10pXG5cbiAgY29uc3Qgc2hvd1N1Ym1pdHRlZFRoZW5DbG9zZSA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBzZXRTdGF0ZSgnc3VibWl0dGVkJylcbiAgICBzZXRUaW1lb3V0KHNldFN0YXRlLCBoaWRlVGhhbmtzQWZ0ZXJNcywgJ2Nsb3NlZCcpXG4gIH0sIFtoaWRlVGhhbmtzQWZ0ZXJNc10pXG5cbiAgY29uc3Qgb3BlbiA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBpZiAoc3RhdGUgIT09ICdjbG9zZWQnKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgc2V0U3RhdGUoJ29wZW4nKVxuICAgIGFwcGVhcmFuY2VJZC5jdXJyZW50ID0gcmFuZG9tVVVJRCgpXG4gICAgdm9pZCBvbk9wZW4oYXBwZWFyYW5jZUlkLmN1cnJlbnQpXG4gIH0sIFtzdGF0ZSwgb25PcGVuXSlcblxuICBjb25zdCBoYW5kbGVTZWxlY3QgPSB1c2VDYWxsYmFjayhcbiAgICAoc2VsZWN0ZWQ6IEZlZWRiYWNrU3VydmV5UmVzcG9uc2UpOiBib29sZWFuID0+IHtcbiAgICA
|