Skip to content

Commit

Permalink
feat: added context for login vals to dynamically update NavBar options.
Browse files Browse the repository at this point in the history
  • Loading branch information
timeowilliams committed Sep 25, 2024
1 parent 5131d26 commit 2b48d32
Show file tree
Hide file tree
Showing 12 changed files with 163 additions and 61 deletions.
6 changes: 3 additions & 3 deletions electron.vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ dotenv.config()

export default defineConfig({
main: {
plugins: [externalizeDepsPlugin(), bytecodePlugin()],
plugins: [externalizeDepsPlugin()],
build: {
outDir: 'out/main',
rollupOptions: {
Expand All @@ -30,13 +30,13 @@ export default defineConfig({
}
},
preload: {
plugins: [externalizeDepsPlugin(), bytecodePlugin()],
plugins: [externalizeDepsPlugin()],
build: {
outDir: 'out/preload',
rollupOptions: {
external: ['electron'],
output: {
format: 'cjs'
format: 'es'
}
}
}
Expand Down
34 changes: 28 additions & 6 deletions src/main/emailService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ dotenv.config()

interface TopSite {
url: string
timeSpent: number
timeSpent: string
}

export class EmailService {
Expand All @@ -31,6 +31,11 @@ export class EmailService {
schedule.scheduleJob('59 23 * * *', () => {
this.sendDailySummary()
})

if (!app.isPackaged) {
// For testing, send the email immediately
this.sendDailySummary()
}
}

private async sendDailySummary(): Promise<void> {
Expand Down Expand Up @@ -92,20 +97,29 @@ export class EmailService {
}
}

private formatTime(timeSpentInMinutes: number): string {
if (timeSpentInMinutes >= 60) {
const hours = Math.floor(timeSpentInMinutes / 60)
const minutes = timeSpentInMinutes % 60
return `${hours}h ${minutes}m`
}
return `${timeSpentInMinutes} minutes`
}

public composeEmailBody(deepWorkHours: number, topSites: TopSite[]): string {
return `
<div style="font-family: Arial, sans-serif; color: #fff; background-color: #000; padding: 20px; border-radius: 8px; max-width: 80%; margin: auto;">
<div style="display: flex; align-items: center; margin-bottom: 20px;">
<h1 style="color: #ecf0f1; margin: 0; font-style: italic; font-family: 'Montserrat', sans-serif;">deepFocus</h1>
</div>
<p style="font-size: 16px; margin-bottom: 20px;">Total Deep Work Hours: <strong>${deepWorkHours}</strong></p>
<h3 style="color: #ecf0f1; border-bottom: 2px solid #ecf0f1; padding-bottom: 10px; margin-top: 30px;">Top 5 Sites Visited</h3>
<h3 style="color: #ecf0f1; border-bottom: 2px solid #ecf0f1; padding-bottom: 10px; margin-top: 30px;">Top 3 Sites Visited</h3>
<ul style="list-style-type: none; padding-left: 0;">
${topSites
.map(
(site) => `
<li style="background-color: #333; margin-bottom: 10px; padding: 10px; border-radius: 4px; border: 1px solid #555; color: #fff;">
<strong>${site.url}</strong>: ${site.timeSpent} minutes
<strong>${site.url}</strong>: ${site.timeSpent}
</li>
`
)
Expand All @@ -123,18 +137,26 @@ export class EmailService {
.filter((tracker) => !unproductiveSites?.includes(tracker.url))
.reduce((total, tracker) => total + tracker.timeSpent, 0)

// Convert ms to hours and return rounded to 1 decimal place
return parseFloat((productiveTime / (1000 * 60 * 60)).toFixed(1))
}

private async getTopSites(): Promise<TopSite[]> {
const siteTimeTrackers = this.store.get('siteTimeTrackers', [])

const getTrimmedTitle = (title: string): string => {
const maxLength = 50
return title.length > maxLength ? title.slice(0, maxLength) + '...' : title
}

// Filter out trackers with zero time, then sort and slice to get top 3
const topSites = siteTimeTrackers
.filter((tracker) => tracker.timeSpent > 0)
.sort((a, b) => b.timeSpent - a.timeSpent)
.slice(0, 3)
.slice(0, 3) // Get top 3
.map((tracker) => ({
url: tracker.url,
timeSpent: Math.round(tracker.timeSpent / (1000 * 60))
url: tracker.url || getTrimmedTitle(tracker.title || 'Unknown Title'),
timeSpent: this.formatTime(Math.round(tracker.timeSpent / (1000 * 60))) // Convert to minutes and format
}))

return topSites
Expand Down
76 changes: 47 additions & 29 deletions src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,25 @@ import Store from 'electron-store'

import { EmailService } from './emailService'
import { StoreSchema, SiteTimeTracker, ExtendedResult } from './types'
import { getUrlFromResult, formatTime, updateSiteTimeTracker } from './productivityUtils'
import {
getUrlFromResult,
formatTime,
updateSiteTimeTracker,
getBaseURL
} from './productivityUtils'

export interface TypedStore extends Store<StoreSchema> {
get<K extends keyof StoreSchema>(key: K): StoreSchema[K]
get<K extends keyof StoreSchema>(key: K, defaultValue: StoreSchema[K]): StoreSchema[K]
set(key: string, value: any): void
delete<K extends keyof StoreSchema>(key: K): void
clear(): void
}

const store = new Store<StoreSchema>() as TypedStore
let currentSiteTimeTrackers: SiteTimeTracker[] = []

setupEnvironment()
console.log('email', process.env.EMAIL)

const emailService = new EmailService(process.env.EMAIL || '', store)

// Initialize environment variables based on the environment
Expand Down Expand Up @@ -70,25 +74,30 @@ function resetCounters(type: 'daily' | 'weekly') {
if (type === 'daily') {
console.log('Resetting Daily Trackers')
currentSiteTimeTrackers.forEach((tracker) => (tracker.timeSpent = 0))
console.log('currentSiteTimeTrackers after daily reset', currentSiteTimeTrackers)
console.log('store.get(lastResetDate)', store.get('lastResetDate'))
store.set('lastResetDate', dayjs().format('YYYY-MM-DD'))
} else if (type === 'weekly') {
console.log('Resetting Weekly Trackers')
currentSiteTimeTrackers.length = 0
currentSiteTimeTrackers = []
}
store.set('siteTimeTrackers', currentSiteTimeTrackers)
}

// Periodic saving of time trackers
function setupPeriodicSave() {
setInterval(() => store.set('siteTimeTrackers', currentSiteTimeTrackers), 5 * 60 * 1000)
console.log('setupPeriodicSave', store.get('siteTimeTrackers'))
}

// Monitor system idle time and user activity
function startActivityMonitoring() {
setInterval(async () => {
const idleTime = powerMonitor.getSystemIdleTime()
if (idleTime > 60) return console.log(`System idle for ${idleTime} seconds.`)

if (idleTime > 60) {
console.log(`System idle for ${idleTime} seconds.`)
return
}
try {
const windowInfo = await activeWindow()
if (windowInfo && windowInfo!.platform === 'macos') {
Expand All @@ -102,14 +111,16 @@ function startActivityMonitoring() {
} catch (error) {
console.error('Error getting active window:', error)
}
}, 60000) // change back to 600000
}, 60000)
}

// Process activity data from active window
function processActivityData(_windowInfoData: ExtendedResult | undefined) {
if (!_windowInfoData) return

if (_windowInfoData?.siteTimeTracker) {
console.log(
`Time spent on ${_windowInfoData.siteTimeTracker.title}: ${formatTime(_windowInfoData.siteTimeTracker.timeSpent)}. URL is ${_windowInfoData.url}`
`Time spent on ${_windowInfoData.siteTimeTracker.title}: ${formatTime(_windowInfoData.siteTimeTracker.timeSpent)}. URL is ${_windowInfoData?.url ? getBaseURL(_windowInfoData?.url) : `not defined`}`
)
console.log(
`Last active timestamp: ${new Date(_windowInfoData.siteTimeTracker.lastActiveTimestamp).toISOString()}`
Expand All @@ -134,9 +145,6 @@ async function createWindow(): Promise<BrowserWindow> {
mainWindow.on('ready-to-show', async () => {
console.log('ready-to-show')
mainWindow.show()
startActivityMonitoring()
currentSiteTimeTrackers = store.get('siteTimeTrackers', [])
emailService.scheduleEmailSend()
})

mainWindow.webContents.setWindowOpenHandler(({ url }) => {
Expand All @@ -160,32 +168,42 @@ app.whenReady().then(async () => {
console.log('ready!')
electronApp.setAppUserModelId('com.electron')

loadUserData()

const now = dayjs()
const lastResetDate = dayjs(store.get('lastResetDate'))

if (!lastResetDate.isSame(now, 'day') || now.diff(lastResetDate, 'hours') > 24) {
console.log('Missed daily reset from previous session, performing now.')
schedulerWorker.postMessage({ type: 'RESET_DAILY' })
}

await createWindow()
await createWindow().then(() => {
loadUserData()
handleDailyReset()
setupPeriodicSave()
setupIPCListeners()
})

setupPeriodicSave()
emailService.scheduleEmailSend()
})

function handleUserLogout() {
store.delete('user')
store.set('siteTimeTrackers', [])
schedulerWorker.postMessage({ type: 'RESET_TRACKERS' })
}
function setupIPCListeners() {
ipcMain.on('send-user-data', (event, user) => {
console.log('Received user data from frontend:', user, event.processId)
handleUserData(user)
const savedUser = store.get('user')
if (savedUser) {
startActivityMonitoring()
currentSiteTimeTrackers = store.get('siteTimeTrackers', [])
emailService.scheduleEmailSend()
}
})
ipcMain.on('test-email-send', async () => await emailService.testEmailSend())
ipcMain.on('logout-user', () => handleUserLogout())
})

function handleUserLogout() {
store.delete('user')
store.set('siteTimeTrackers', [])
schedulerWorker.postMessage({ type: 'RESET_TRACKERS' })
}
function handleDailyReset() {
const now = dayjs()
const lastResetDate = dayjs(store.get('lastResetDate'))
if (!lastResetDate.isSame(now, 'day') || now.diff(lastResetDate, 'hours') > 24) {
console.log('Missed daily reset from previous session, performing now.')
schedulerWorker.postMessage({ type: 'RESET_DAILY' })
}
}

app.on('before-quit', () => schedulerWorker.terminate())
Expand Down
33 changes: 30 additions & 3 deletions src/main/productivityUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ function getDomainFromUrl(url: string): string {
return parsedUrl.hostname || ''
}

export function getBaseURL(url: string): string | null {
try {
const urlObj = new URL(url)
return `${urlObj.protocol}//${urlObj.hostname}` // This gives you the base URL
} catch (error) {
console.error('Invalid URL:', error)
return null
}
}

function isProductiveUrl(url: string): boolean {
const domain = getDomainFromUrl(url)
console.log(
Expand Down Expand Up @@ -63,6 +73,15 @@ export function formatUrl(input: string): string {
}
}

function isValidURL(url: string): boolean {
try {
new URL(url) // URL constructor will throw an error if it's not a valid URL
return true
} catch (_) {
return false
}
}

export function formatTime(milliseconds: number): string {
const seconds = Math.floor(milliseconds / 1000)
const minutes = Math.floor(seconds / 60)
Expand All @@ -84,16 +103,24 @@ export function updateSiteTimeTracker(
const currentTime = Date.now()
const url = getUrlFromResult(windowInfo) || windowInfo.title

let tracker = timeTrackers.find((t) => t.url === url)
// Check if this is a URL or an app (if URL extraction fails)
let trackerKey = url
if (url && isValidURL(url)) {
trackerKey = getBaseURL(url) as string // Use base URL to track all paths under one domain
} else {
trackerKey = windowInfo.title || 'Unknown App'
}

let tracker = timeTrackers.find((t) => t.url === trackerKey)
if (tracker) {
console.log('Updating existing tracker')
tracker.timeSpent += currentTime - tracker.lastActiveTimestamp
tracker.lastActiveTimestamp = currentTime
} else {
console.log('Creating new tracker')
tracker = {
url,
title: windowInfo.title,
url: trackerKey,
title: windowInfo.title || 'Unknown App',
timeSpent: 0,
lastActiveTimestamp: currentTime
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ setInterval(() => {
} else {
console.log('Worker is running, but no username set yet.')
}
}, 30000)
}, 60000)

// Schedule daily reset at midnight
schedule.scheduleJob('0 0 * * *', () => {
Expand Down
25 changes: 14 additions & 11 deletions src/renderer/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { lazy, Suspense, onMount, createSignal } from 'solid-js'
import { lazy, Suspense, onMount, createSignal, ComponentProps } from 'solid-js'
import { Router, Route, A, useLocation } from '@solidjs/router'
import { render } from 'solid-js/web'
import { AuthProvider, useAuth } from './lib/AuthContext'

import { sendUserToBackend } from './lib/utils'
import './assets/main.css'
Expand All @@ -12,8 +13,8 @@ const Signup = lazy(() => import('./Signup'))
const Versions = lazy(() => import('./Versions'))
const HelloWorld = () => <h1>Hello World!</h1>

const App = (props) => {
const [isLoggedIn, setIsLoggedIn] = createSignal(false)
const App = (props: ComponentProps<typeof Router>) => {
const [isLoggedIn, setIsLoggedIn] = useAuth()
const [isNewUser, setIsNewUser] = createSignal(true)
const location = useLocation()

Expand Down Expand Up @@ -83,14 +84,16 @@ const App = (props) => {

render(
() => (
<Router root={App}>
<Suspense fallback={<div>Loading...</div>}>
<Route path="/" component={Versions} />
<Route path="/login" component={Login} />
<Route path="/hello-world" component={HelloWorld} />
<Route path="/signup" component={Signup} />
</Suspense>
</Router>
<AuthProvider>
<Router root={App}>
<Suspense fallback={<div>Loading...</div>}>
<Route path="/" component={Versions} />
<Route path="/login" component={Login} />
<Route path="/hello-world" component={HelloWorld} />
<Route path="/signup" component={Signup} />
</Suspense>
</Router>
</AuthProvider>
),
document.getElementById('root') as HTMLElement
)
Loading

0 comments on commit 2b48d32

Please sign in to comment.