From f27527aa130ebae355a61de443c42dde1b71dc46 Mon Sep 17 00:00:00 2001 From: BP602 <3460479+BP602@users.noreply.github.com> Date: Thu, 2 Oct 2025 14:48:23 +0200 Subject: [PATCH 1/2] fix: throttle analytics prompt to monthly --- .../assets/styles/components/Chat/Input.scss | 70 +++++++++ .../src/components/Chat/Input/index.jsx | 144 +++++++++++++++++- 2 files changed, 213 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/assets/styles/components/Chat/Input.scss b/src/renderer/src/assets/styles/components/Chat/Input.scss index 7835e5c..bbdbce9 100644 --- a/src/renderer/src/assets/styles/components/Chat/Input.scss +++ b/src/renderer/src/assets/styles/components/Chat/Input.scss @@ -42,6 +42,76 @@ left: 0; z-index: 1; + > .chatTelemetryPrompt { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; + width: calc(100% - 16px); + margin: 0 8px 8px; + padding: 10px 12px; + border: 1px solid var(--border-secondary); + border-radius: 6px; + background: var(--bg-tertiary); + color: var(--text-primary); + animation: slideAndFadeIn 0.2s ease-in-out forwards; + + > .chatTelemetryPromptText { + display: flex; + flex-direction: column; + gap: 2px; + font-size: 13px; + line-height: 1.4; + + > strong { + font-size: 14px; + color: var(--text-primary); + } + + > span { + color: var(--text-secondary); + } + } + + > .chatTelemetryPromptActions { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; + + > button { + cursor: pointer; + border-radius: 4px; + padding: 4px 10px; + font-size: 12px; + transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out, color 0.2s ease-in-out; + border: 1px solid transparent; + } + + > .chatTelemetryPromptEnable { + background: var(--btn-primary-bg); + color: var(--btn-primary-text); + border-color: var(--btn-primary-bg); + + &:hover { + background: var(--btn-primary-hover); + border-color: var(--btn-primary-hover); + } + } + + > .chatTelemetryPromptDismiss { + background: var(--btn-secondary-bg); + border-color: var(--btn-secondary-border); + color: var(--text-tertiary); + + &:hover { + color: var(--text-primary); + border-color: var(--btn-primary-hover); + } + } + } + } + > .chatInfoBar { display: flex; align-items: center; diff --git a/src/renderer/src/components/Chat/Input/index.jsx b/src/renderer/src/components/Chat/Input/index.jsx index ec26464..0111322 100644 --- a/src/renderer/src/components/Chat/Input/index.jsx +++ b/src/renderer/src/components/Chat/Input/index.jsx @@ -39,6 +39,7 @@ import { MessageParser } from "../../../utils/MessageParser"; import { isModeEnabled, chatModeMatches } from "../../../utils/ChatUtils"; import { recordChatModeFeatureUsage } from "../../../telemetry/chatModeTelemetry"; import { useAccessibleKickEmotes } from "./useAccessibleKickEmotes"; +import { useSettings } from "../../../providers/SettingsProvider"; const onError = (error) => { console.error(error); @@ -117,6 +118,71 @@ const isEmoteOnlyContent = (editor) => { const messageHistory = new Map(); +const TELEMETRY_PROMPT_DISMISSED_KEY = "kicktalk.telemetryPromptDismissed"; +const TELEMETRY_PROMPT_DISMISSAL_TTL_MS = 1000 * 60 * 60 * 24 * 30; // 30 days + +const persistTelemetryPromptDismissal = () => { + if (typeof window === "undefined") return null; + + const record = { dismissedAt: new Date().toISOString() }; + + try { + window.localStorage.setItem( + TELEMETRY_PROMPT_DISMISSED_KEY, + JSON.stringify(record), + ); + } catch (error) { + console.warn("[ChatInput]: Failed to persist telemetry prompt dismissal", error); + return null; + } + + return record; +}; + +const readTelemetryPromptDismissal = () => { + if (typeof window === "undefined") return null; + + try { + const storedValue = window.localStorage.getItem(TELEMETRY_PROMPT_DISMISSED_KEY); + if (!storedValue) return null; + + if (storedValue === "true") { + // migrate legacy boolean flag to structured record + return persistTelemetryPromptDismissal(); + } + + try { + const parsed = JSON.parse(storedValue); + if (parsed?.dismissedAt) { + const dismissedAt = new Date(parsed.dismissedAt); + if (!Number.isNaN(dismissedAt.getTime())) { + return { dismissedAt: dismissedAt.toISOString() }; + } + } + } catch { + // fall through to attempt parsing a plain ISO string + } + + const asDate = new Date(storedValue); + if (!Number.isNaN(asDate.getTime())) { + const record = { dismissedAt: asDate.toISOString() }; + try { + window.localStorage.setItem( + TELEMETRY_PROMPT_DISMISSED_KEY, + JSON.stringify(record), + ); + } catch (error) { + console.warn("[ChatInput]: Failed to migrate telemetry prompt dismissal", error); + } + return record; + } + } catch (error) { + console.warn("[ChatInput]: Failed to read telemetry prompt dismissal state", error); + } + + return null; +}; + const EmoteSuggestions = memo( ({ suggestions, onSelect, selectedIndex, userChatroomInfo }) => { const suggestionsRef = useRef(null); @@ -1160,6 +1226,7 @@ const ReplyHandler = ({ chatroomId, getReplyData, clearReplyData, allStvEmotes, const ChatInput = memo( ({ chatroomId, isReplyThread = false, replyMessage = {}, settings }) => { + const { updateSettings: updateAppSettings } = useSettings(); const sendMessage = useChatStore((state) => state.sendMessage); const sendReply = useChatStore((state) => state.sendReply); const clearDraftMessage = useChatStore((state) => state.clearDraftMessage); @@ -1183,6 +1250,57 @@ const ChatInput = memo( const allStvEmotes = useAllStvEmotes(chatroomId); + const [showTelemetryPrompt, setShowTelemetryPrompt] = useState(false); + + useEffect(() => { + if (!settings) { + setShowTelemetryPrompt(false); + return; + } + + if (settings?.telemetry?.enabled) { + setShowTelemetryPrompt(false); + return; + } + + const dismissalRecord = readTelemetryPromptDismissal(); + if (!dismissalRecord?.dismissedAt) { + setShowTelemetryPrompt(true); + return; + } + + const dismissedAt = new Date(dismissalRecord.dismissedAt); + if (Number.isNaN(dismissedAt.getTime())) { + setShowTelemetryPrompt(true); + return; + } + + const now = Date.now(); + const age = now - dismissedAt.getTime(); + setShowTelemetryPrompt(age >= TELEMETRY_PROMPT_DISMISSAL_TTL_MS); + }, [settings, settings?.telemetry?.enabled]); + + const handleEnableTelemetry = useCallback(async () => { + try { + await updateAppSettings?.("telemetry", { + ...settings?.telemetry, + enabled: true, + }); + } catch (error) { + console.warn("[ChatInput]: Failed to enable telemetry from prompt", error); + } + + persistTelemetryPromptDismissal(); + + setShowTelemetryPrompt(false); + }, [settings?.telemetry, updateAppSettings]); + + const handleDismissTelemetryPrompt = useCallback(() => { + persistTelemetryPromptDismissal(); + + setShowTelemetryPrompt(false); + }, []); + // Reset selected index when changing chatrooms useEffect(() => { const history = messageHistory.get(chatroomId); @@ -1316,6 +1434,29 @@ const ChatInput = memo( return (