diff --git a/README.md b/README.md index 1022236..2800579 100644 --- a/README.md +++ b/README.md @@ -31,5 +31,4 @@ Ebb uses your window, mouse and keyboard activity to track your workflow habits. The monitoring service is a Rust application that runs on your computer and is responsible for recording your activities. It is designed to be run as a background service and will not interfere with your workflow. Makes use of the os-monitor crate to monitor your activities and then record them to the database on your device. # [Monitor](https://github.com/CodeClimbersIO/os-monitor) - The monitor is a Rust application that runs on your computer and is responsible for monitoring your activities. It is specifically responsible for monitoring (but not recording) your window, mouse and keyboard activity. diff --git a/src/api/ebbApi/blockingPreferenceApi.ts b/src/api/ebbApi/blockingPreferenceApi.ts index 839712d..b262d13 100644 --- a/src/api/ebbApi/blockingPreferenceApi.ts +++ b/src/api/ebbApi/blockingPreferenceApi.ts @@ -121,17 +121,17 @@ const getWorkflowBlockedApps = async (workflowId: string): Promise => { const defaultCategoryNames = ['social media', 'entertainment'] const allCategoryTags = await TagRepo.getTagsByType('category') - const defaultTags = allCategoryTags.filter((tag: Tag) => + const defaultTags = allCategoryTags.filter((tag: Tag) => defaultCategoryNames.includes(tag.name) ) const defaultTagIds = defaultTags.map((tag: Tag) => tag.id).filter((id): id is string => !!id) - + let defaultSearchOptions: SearchOption[] = [] if (defaultTagIds.length > 0) { const categoriesWithCounts = await TagRepo.getCategoriesWithAppCounts(defaultTagIds) defaultSearchOptions = categoriesWithCounts.map((catInfo: TagWithAppCount): SearchOption => ({ type: 'category', - tag: catInfo, + tag: catInfo, category: catInfo.name as AppCategory, count: catInfo.count })) diff --git a/src/hooks/useNotificationListener.ts b/src/hooks/useNotificationListener.ts index c0c72d5..c0f0c95 100644 --- a/src/hooks/useNotificationListener.ts +++ b/src/hooks/useNotificationListener.ts @@ -7,6 +7,25 @@ import { useNavigate } from 'react-router-dom' import { SmartSessionApi } from '@/api/ebbApi/smartSessionApi' import { useFlowTimer } from '@/lib/stores/flowTimer' import { FlowSessionApi } from '@/api/ebbApi/flowSessionApi' +import { AnalyticsService } from '@/lib/analytics' + +interface BlockedApp { + app_name: string + app_external_id: string +} + +const trackBlockedApps = (blockedApps: BlockedApp[], workflowId?: string, workflowName?: string) => { + blockedApps.forEach((app) => { + // For websites, use app_external_id (the URL), for apps use app_name + const blockedName = app.app_external_id || app.app_name + + AnalyticsService.trackEvent('app_or_website_block_attempt', { + blocked_item_name: blockedName, + workflow_id: workflowId, + workflow_name: workflowName, + }) + }) +} export const useNotificationListener = () => { const navigate = useNavigate() @@ -55,17 +74,26 @@ export const useNotificationListener = () => { window.dispatchEvent(new Event('end-session')) }) }) - unlistenAppBlocked = await listen('on-app-blocked', async () => { + unlistenAppBlocked = await listen('on-app-blocked', async (event: { payload: { blocked_apps: BlockedApp[] } }) => { info('App: app blocked') EbbWorker.debounceWork(async () => { try { const session = await FlowSessionApi.getInProgressFlowSessionWithWorkflow() const isHardMode = session?.workflow_json?.settings.difficulty === 'hard' + if (isHardMode) { await invoke('show_notification', { notificationType: 'blocked-app-hard' }) } else { await invoke('show_notification', { notificationType: 'blocked-app' }) } + + if (event.payload?.blocked_apps) { + trackBlockedApps( + event.payload.blocked_apps, + session?.workflow_id, + session?.workflow_json?.name + ) + } } catch (error) { console.error(`Error getting in progress flow session with workflow: ${error}`, error) } diff --git a/src/hooks/usePermissions.ts b/src/hooks/usePermissions.ts index 7907470..cf33e98 100644 --- a/src/hooks/usePermissions.ts +++ b/src/hooks/usePermissions.ts @@ -3,13 +3,29 @@ import { useAuth } from './useAuth' import { defaultPermissions } from '@/api/ebbApi/licenseApi' import { usePermissionsStore } from '@/lib/stores/permissionsStore' import { useEffect } from 'react' +import { isDev } from '@/lib/utils/environment.util' + +const devPermissions = { + canUseHardDifficulty: true, + canUseAllowList: true, + canUseMultipleProfiles: true, + canUseMultipleDevices: true, + canUseSmartFocus: true, + canUseSlackIntegration: true, + canScheduleSessions: true, + hasProAccess: true, +} export const usePermissions = () => { const { user } = useAuth() const { data: licenseData } = useLicenseWithDevices(user?.id || null) const setPermissions = usePermissionsStore((state) => state.setPermissions) - const permissions = licenseData?.permissions || defaultPermissions + // In dev mode, grant all pro permissions + const isDevMode = isDev() + const permissions = isDevMode + ? devPermissions + : (licenseData?.permissions || defaultPermissions) // Sync to store whenever permissions change useEffect(() => { diff --git a/src/lib/analytics.ts b/src/lib/analytics.ts index c813b3e..a18a9d4 100644 --- a/src/lib/analytics.ts +++ b/src/lib/analytics.ts @@ -135,6 +135,9 @@ export type AnalyticsEvent = | 'activity_history_delete_all_clicked' | 'activity_history_page_changed' + // Blocking Events + | 'app_or_website_block_attempt' + export interface AnalyticsEventProperties { // Focus session properties difficulty?: 'easy' | 'medium' | 'hard' @@ -157,6 +160,9 @@ export interface AnalyticsEventProperties { // Billing properties days_remaining?: number + + // Block attempt properties + blocked_item_name?: string } const trackEvent = (