From 71d38d64564f86ba35fa2312d3b00d9a0d888356 Mon Sep 17 00:00:00 2001 From: benz Date: Fri, 1 Nov 2024 18:19:31 +0100 Subject: [PATCH 1/9] Set button size to icon close #239 --- src/popup/components/Leaderboard.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/popup/components/Leaderboard.tsx b/src/popup/components/Leaderboard.tsx index fe75d09f..af579ec9 100644 --- a/src/popup/components/Leaderboard.tsx +++ b/src/popup/components/Leaderboard.tsx @@ -1,7 +1,9 @@ -import React, { useState, useEffect } from 'react'; -import { Link } from 'react-router-dom'; import { type ClassValue } from 'clsx'; import { Medal, ChevronLeft, ChevronRight } from 'lucide-react'; +import React, { useState, useEffect } from 'react'; +import { Link } from 'react-router-dom'; +import { Button } from '@/components/ui/button'; +import { Skeleton } from '@/components/ui/skeleton'; import { Table, TableBody, @@ -11,17 +13,15 @@ import { TableHeader, TableRow, } from '@/components/ui/table'; -import { Skeleton } from '@/components/ui/skeleton'; -import remote from '@/remote'; -import { RemoteResponse } from '@/const'; -import { cn } from '@/utils'; -import { Button } from '@/components/ui/button'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '@/components/ui/tooltip'; +import { RemoteResponse } from '@/const'; +import remote from '@/remote'; +import { cn } from '@/utils'; const limit = 10; const maxPages = 10; @@ -88,7 +88,7 @@ export default () => { - - - - - )} - /> - )} + ); }; + +export default ConfigForm; From b200d1e407b8a9a768cff1bcdf2363df8dbe324b Mon Sep 17 00:00:00 2001 From: benz Date: Thu, 7 Nov 2024 19:26:48 +0100 Subject: [PATCH 6/9] the snooze button has been made --- src/background/background.ts | 33 ++----- src/const.ts | 3 +- src/popup/components/forms/LocalSettings.tsx | 95 +++++++++++--------- src/utils.ts | 13 ++- tests/__mocks__/webextension-polyfill.ts | 1 + 5 files changed, 70 insertions(+), 75 deletions(-) diff --git a/src/background/background.ts b/src/background/background.ts index 4a069a73..69c766a6 100644 --- a/src/background/background.ts +++ b/src/background/background.ts @@ -9,6 +9,7 @@ import { random, getBrowser, isRunningInBackground, + isInSnooze, sendMessage, } from '@/utils'; @@ -33,28 +34,6 @@ const updateBadgeColors = () => { })(); }; -// Snooze state variable to track if the spawn is snoozed -let isSnoozed = false; -let snoozeTimer: NodeJS.Timeout | null = null; - -// Function to handle starting the snooze -const startSnooze = async (duration: number) => { - // Activate snooze - isSnoozed = true; - - // Clear existing timer if present - if (snoozeTimer) clearTimeout(snoozeTimer); - - // If snooze is not indefinite (-1), set up a timeout to end snooze - if (duration !== -1) { - snoozeTimer = setTimeout(() => { - isSnoozed = false; - snoozeTimer = null; - log.info('Snooze ended, resuming balloon spawns'); - }, duration); - } -}; - (() => { // Check if the background script is running in the background if (!isRunningInBackground()) return; @@ -78,6 +57,7 @@ const startSnooze = async (duration: number) => { log.getLevel(), ')' ); + log.debug(''); // Clear all alarms await browser.alarms.clearAll(); @@ -124,12 +104,6 @@ const startSnooze = async (duration: number) => { }; const spawnBalloon = async () => { - // Prevent spawn if snooze is active - if (isSnoozed) { - log.info('Balloon spawn snoozed'); - return; - } - log.groupCollapsed( 'debug', `(${new Date().toLocaleTimeString()}) Spawning Balloon...` @@ -157,6 +131,9 @@ const startSnooze = async (duration: number) => { } log.debug(' - Last spawn was not too recent'); + if (await isInSnooze()) return skipSpawnMessage('In snooze'); + log.debug(' - Not in snooze'); + // Check if the browser is idle const state = await browser.idle.queryState(5 * 60); if (state !== 'active') return skipSpawnMessage('Browser is idle'); diff --git a/src/const.ts b/src/const.ts index 218937c7..d0359318 100644 --- a/src/const.ts +++ b/src/const.ts @@ -110,6 +110,7 @@ export type SyncStorageStructure = { config: Config; token: string; user: User; + snooze: number | null; }; export type SyncStorageKey = keyof SyncStorageStructure; @@ -142,7 +143,7 @@ type SetLogLevelMessage = { }; type StartSnoozeMessage = { - action: 'startSnooze'; + action: 'setSnooze'; duration: number; }; diff --git a/src/popup/components/forms/LocalSettings.tsx b/src/popup/components/forms/LocalSettings.tsx index 6b451a1f..d31a2dd5 100644 --- a/src/popup/components/forms/LocalSettings.tsx +++ b/src/popup/components/forms/LocalSettings.tsx @@ -27,7 +27,7 @@ import { Slider } from '@/components/ui/slider'; import { initalConfig } from '@/const'; import log from '@/managers/log'; import storage from '@/managers/storage'; -import { askOriginPermissions } from '@/utils'; +import { askOriginPermissions, isInSnooze } from '@/utils'; const MIN_POP_VOLUME = 0; const VOLUME_STEP = 20; @@ -40,14 +40,15 @@ const formSchema = z.object({ popVolume: z.number().int().min(MIN_POP_VOLUME).max(MAX_POP_VOLUME), spawnRate: z.number().int().min(MIN_SPAWN_RATE).max(MAX_SPAWN_RATE), fullScreenVideoSpawn: z.boolean(), - snooze: z.number().int().min(0), + snooze: z.number().int().min(-1).nullable(), permissions: z.object({ origins: z.array(z.string()), permissions: z.array(z.string()), }), }); -const SNOOZE_OPTIONS = [ +const SNOOZE_OPTIONS: { label: string; value: number | null }[] = [ + { label: 'Off', value: null }, { label: '15 minutes', value: 15 * 60 * 1000 }, { label: '1 hour', value: 1 * 60 * 60 * 1000 }, { label: '3 hours', value: 3 * 60 * 60 * 1000 }, @@ -56,7 +57,7 @@ const SNOOZE_OPTIONS = [ { label: 'Until I turn it back on', value: -1 }, ]; -const ConfigForm = () => { +export default () => { const [popVolume, setPopVolume] = useState(initalConfig.popVolume); const [spawnRate, setSpawnRate] = useState(initalConfig.spawnRate); const [fullScreenVideoSpawn, setFullScreenVideoSpawn] = useState( @@ -65,8 +66,6 @@ const ConfigForm = () => { const [permissions, setPermissions] = useState( {} ); - const [isSnoozed, setIsSnoozed] = useState(false); - const [snoozeTimer, setSnoozeTimer] = useState(null); const form = useForm>({ resolver: zodResolver(formSchema), @@ -105,32 +104,20 @@ const ConfigForm = () => { setPermissions(await browser.permissions.getAll()); }; - const startSnooze = (duration: number) => { - setIsSnoozed(true); - setSpawnRate(0); - - if (duration === -1) { - return; - } - - const timer = setTimeout(() => { - setSpawnRate(initalConfig.spawnRate); - setIsSnoozed(false); - form.setValue('snooze', 0); - }, duration); - - setSnoozeTimer(timer); - }; - - const handleSnoozeChange = (selectedDuration: string) => { - const duration = Number(selectedDuration); + const handleSnoozeChange = async (selectedDuration: string) => { + const duration = + selectedDuration === 'null' ? null : Number(selectedDuration); form.setValue('snooze', duration); - startSnooze(duration); + + const snoozeEnd = + duration === null ? null : duration > 0 ? Date.now() + duration : -1; + await storage.sync.set('snooze', snoozeEnd); }; useEffect(() => { const loadConfig = async () => { const config = await storage.sync.get('config'); + setPopVolume(config.popVolume); setSpawnRate(config.spawnRate); setFullScreenVideoSpawn(config.fullScreenVideoSpawn); @@ -138,13 +125,7 @@ const ConfigForm = () => { }; loadConfig(); - - return () => { - if (snoozeTimer) { - clearTimeout(snoozeTimer); - } - }; - }, [snoozeTimer]); + }, []); return (
@@ -265,14 +246,14 @@ const ConfigForm = () => { control={form.control} name="fullScreenVideoSpawn" render={({ field: { onChange } }) => ( - + Full-Screen Video Spawn { - onFullScreenVideoSpawnChange(checked); - onChange(checked); + onCheckedChange={(val) => { + onFullScreenVideoSpawnChange(!!val); + onChange(!!val); }} /> @@ -280,12 +261,42 @@ const ConfigForm = () => { )} /> - + {/* If the user hasn't granted the host permissions; show the grant permission button */} + {!(permissions.origins?.length !== 0) && ( + ( + + + Host Permission + +

+ Host Permission{' '} + *recommended +

+

+ Pop-a-loon requires host permissions to function properly. +

+
+
+ + + + + + +
+ )} + /> + )} ); }; - -export default ConfigForm; diff --git a/src/utils.ts b/src/utils.ts index 4d2a370f..6dbfbd24 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -159,7 +159,7 @@ export async function askOriginPermissions() { log.debug('Permissions granted for', permissions); } -export const isFullScreenVideoPlaying = () => { +export function isFullScreenVideoPlaying() { const fullscreenElement = document.fullscreenElement; if (fullscreenElement) { if (fullscreenElement.tagName.toLowerCase() === 'video') { @@ -174,9 +174,14 @@ export const isFullScreenVideoPlaying = () => { } } return false; -}; +} + +export async function isInSnooze(): Promise { + const snooze = await storage.sync.get('snooze'); + return snooze !== null && (snooze === -1 || Date.now() < snooze); +} -export const joinPaths = (...paths: string[]): string => { +export function joinPaths(...paths: string[]): string { return paths .map((part, index) => { if (index === 0) { @@ -187,4 +192,4 @@ export const joinPaths = (...paths: string[]): string => { }) .filter((part) => part.length) .join('/'); -}; +} diff --git a/tests/__mocks__/webextension-polyfill.ts b/tests/__mocks__/webextension-polyfill.ts index fe58c747..4b829ba9 100644 --- a/tests/__mocks__/webextension-polyfill.ts +++ b/tests/__mocks__/webextension-polyfill.ts @@ -11,6 +11,7 @@ const defaultStorage: SyncStorageStructure = { updatedAt: '', createdAt: '', }, + snooze: null, }; let mockStorage: SyncStorageStructure = defaultStorage; From a215cb5266b7db7784170141e8c854f7022b8dc1 Mon Sep 17 00:00:00 2001 From: SimonStnn Date: Fri, 8 Nov 2024 23:41:06 +0100 Subject: [PATCH 7/9] Fix snooze functionallity in LocalSettings form #346 --- src/popup/components/forms/LocalSettings.tsx | 202 +++++++++++++------ 1 file changed, 138 insertions(+), 64 deletions(-) diff --git a/src/popup/components/forms/LocalSettings.tsx b/src/popup/components/forms/LocalSettings.tsx index d31a2dd5..dc0ade8b 100644 --- a/src/popup/components/forms/LocalSettings.tsx +++ b/src/popup/components/forms/LocalSettings.tsx @@ -21,13 +21,13 @@ import { SelectValue, SelectContent, SelectItem, - SelectLabel, } from '@/components/ui/select'; +import { Skeleton } from '@/components/ui/skeleton'; import { Slider } from '@/components/ui/slider'; import { initalConfig } from '@/const'; import log from '@/managers/log'; import storage from '@/managers/storage'; -import { askOriginPermissions, isInSnooze } from '@/utils'; +import { askOriginPermissions } from '@/utils'; const MIN_POP_VOLUME = 0; const VOLUME_STEP = 20; @@ -35,29 +35,29 @@ const MAX_POP_VOLUME = 100; const MIN_SPAWN_RATE = 0.1; const MAX_SPAWN_RATE = 1; const SPAWN_RATE_STEP = 0.1; +const SNOOZE_OPTIONS: { label: string; value: number | null }[] = [ + { label: 'Off', value: null }, + { label: '15 minutes', value: 15 * 60 * 1000 }, + { label: '1 hour', value: 1 * 60 * 60 * 1000 }, + { label: '3 hours', value: 3 * 60 * 60 * 1000 }, + { label: '8 hours', value: 8 * 60 * 60 * 1000 }, + { label: '24 hours', value: 24 * 60 * 60 * 1000 }, + { label: 'Until I turn it back on', value: -1 }, +]; const formSchema = z.object({ popVolume: z.number().int().min(MIN_POP_VOLUME).max(MAX_POP_VOLUME), spawnRate: z.number().int().min(MIN_SPAWN_RATE).max(MAX_SPAWN_RATE), fullScreenVideoSpawn: z.boolean(), - snooze: z.number().int().min(-1).nullable(), + snooze: z.number().int().min(-1).nullable().optional(), permissions: z.object({ origins: z.array(z.string()), permissions: z.array(z.string()), }), }); -const SNOOZE_OPTIONS: { label: string; value: number | null }[] = [ - { label: 'Off', value: null }, - { label: '15 minutes', value: 15 * 60 * 1000 }, - { label: '1 hour', value: 1 * 60 * 60 * 1000 }, - { label: '3 hours', value: 3 * 60 * 60 * 1000 }, - { label: '8 hours', value: 8 * 60 * 60 * 1000 }, - { label: '24 hours', value: 24 * 60 * 60 * 1000 }, - { label: 'Until I turn it back on', value: -1 }, -]; - export default () => { + const [isConfigLoaded, setIsConfigLoaded] = useState(false); const [popVolume, setPopVolume] = useState(initalConfig.popVolume); const [spawnRate, setSpawnRate] = useState(initalConfig.spawnRate); const [fullScreenVideoSpawn, setFullScreenVideoSpawn] = useState( @@ -66,6 +66,7 @@ export default () => { const [permissions, setPermissions] = useState( {} ); + const [snooze, setSnooze] = useState(null); const form = useForm>({ resolver: zodResolver(formSchema), @@ -104,14 +105,45 @@ export default () => { setPermissions(await browser.permissions.getAll()); }; + const updateSnooze = async ( + snoozeEnd: number | null, + saveToStorage: boolean = true + ) => { + setSnooze(snoozeEnd); + if (saveToStorage) await storage.sync.set('snooze', snoozeEnd); + console.debug('Snooze changed to', snoozeEnd); + + const snoozeOption = SNOOZE_OPTIONS.find( + // Can only detect off or until I turn it back on + (option) => option.value === snoozeEnd + ); + console.debug('Snooze option is', snoozeOption); + form.setValue('snooze', snoozeOption?.value); + }; + const handleSnoozeChange = async (selectedDuration: string) => { const duration = selectedDuration === 'null' ? null : Number(selectedDuration); - form.setValue('snooze', duration); - const snoozeEnd = duration === null ? null : duration > 0 ? Date.now() + duration : -1; - await storage.sync.set('snooze', snoozeEnd); + await updateSnooze(snoozeEnd); + }; + + const renderSnoozeStatus = () => { + if (snooze !== null && snooze > 0) { + return ( + <> + Snoozed until{' '} + {new Date(snooze).toLocaleTimeString('en-US', { + hour: 'numeric', + minute: 'numeric', + })} + + ); + } else if (snooze === -1) { + return <>Snoozed until you turn it back off; + } + return <>Snooze is off; }; useEffect(() => { @@ -122,6 +154,8 @@ export default () => { setSpawnRate(config.spawnRate); setFullScreenVideoSpawn(config.fullScreenVideoSpawn); setPermissions(await browser.permissions.getAll()); + await updateSnooze(await storage.sync.get('snooze'), false); + setIsConfigLoaded(true); // Set loading status to true after config is loaded }; loadConfig(); @@ -130,47 +164,69 @@ export default () => { return (
- ( - - - Snooze Balloon Spawn - -

- Snooze Baloon Spawn -

-

- Set how long to pause the balloon appearances. During this - snooze period, no new balloons will spawn on your screen. -

-
-
- - - - -
- )} - /> + {!isConfigLoaded ? ( + + ) : ( + ( + + + Snooze Balloon Spawn + +

+ Snooze Baloon Spawn +

+

+ Set how long to pause the balloon appearances. During this + snooze period, no new balloons will spawn on your screen. +

+

+ {renderSnoozeStatus()} +

+
+
+ + + + +
+ )} + /> + )} { Full-Screen Video Spawn - { - onFullScreenVideoSpawnChange(!!val); - onChange(!!val); - }} - /> + + { + onFullScreenVideoSpawnChange(!!val); + onChange(!!val); + }} + /> + +

+ Fullscreen video spawn +

+

+ Weither or not to spawn balloons in fullscreen video + players, like youtube. +

+

+ {fullScreenVideoSpawn ? ( + <>Balloons can spawn! + ) : ( + <>Balloons will not spawn. + )} +

+
+
From 1fb073c4c56999c0ac8db19b65ca2dcb9fc09e49 Mon Sep 17 00:00:00 2001 From: SimonStnn Date: Sat, 9 Nov 2024 00:01:52 +0100 Subject: [PATCH 8/9] Return unwated deleted code --- src/popup/components/forms/LocalSettings.tsx | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/popup/components/forms/LocalSettings.tsx b/src/popup/components/forms/LocalSettings.tsx index dc0ade8b..9daeeef0 100644 --- a/src/popup/components/forms/LocalSettings.tsx +++ b/src/popup/components/forms/LocalSettings.tsx @@ -78,8 +78,12 @@ export default () => { const config = await storage.sync.get('config'); await storage.sync.set('config', { ...config, popVolume }); setPopVolume(popVolume); - log.debug('Pop volume changed to', popVolume); + log.debug( + 'Pop volume changed to', + (await storage.sync.get('config')).popVolume + ); + // Play the pop sound popSound.volume = popVolume / 100; popSound.play(); }; @@ -88,7 +92,10 @@ export default () => { const config = await storage.sync.get('config'); await storage.sync.set('config', { ...config, spawnRate }); setSpawnRate(spawnRate); - log.debug('Spawn rate changed to', spawnRate); + log.debug( + 'Spawn rate changed to', + (await storage.sync.get('config')).spawnRate + ); }; const onFullScreenVideoSpawnChange = async ( @@ -97,7 +104,10 @@ export default () => { const config = await storage.sync.get('config'); await storage.sync.set('config', { ...config, fullScreenVideoSpawn }); setFullScreenVideoSpawn(fullScreenVideoSpawn); - log.debug('Spawning in full screen video players:', fullScreenVideoSpawn); + log.debug( + 'Spawning in full screen video players:', + (await storage.sync.get('config')).fullScreenVideoSpawn + ); }; const onGrantOriginPermissionClick = async () => { From 822cb39c1b9ee2a4c02c4d3ef7a7bfba700bf541 Mon Sep 17 00:00:00 2001 From: SimonStnn Date: Sat, 9 Nov 2024 00:02:02 +0100 Subject: [PATCH 9/9] Remove unused message type --- src/const.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/const.ts b/src/const.ts index d0359318..28397d31 100644 --- a/src/const.ts +++ b/src/const.ts @@ -142,17 +142,11 @@ type SetLogLevelMessage = { level: LogLevelNumbers; }; -type StartSnoozeMessage = { - action: 'setSnooze'; - duration: number; -}; - export type Message = | UpdateCounterMessage | IncrementCount | SpawnBalloonMessage - | SetLogLevelMessage - | StartSnoozeMessage; + | SetLogLevelMessage; // // * Alarms