Skip to content

Commit

Permalink
Fix build issue, improve experience sampling
Browse files Browse the repository at this point in the history
  • Loading branch information
SRichner committed Feb 19, 2024
1 parent c0cc520 commit 2619ba9
Show file tree
Hide file tree
Showing 12 changed files with 139 additions and 67 deletions.
50 changes: 42 additions & 8 deletions src/electron/electron/ipc/Api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,47 @@ import { TypedIpcMain } from './TypedIpcMain';
import Events from '../shared/Events';
import Commands from '../shared/Commands';
import { ExperienceSamplingService } from '../main/services/ExperienceSamplingService';
import { ipcMain } from 'electron';
import { ipcMain, IpcMainInvokeEvent } from 'electron';
import { WindowService } from '../main/services/WindowService';
import { getLogger } from '../shared/Logger';

const typedIpcMain: TypedIpcMain<Events, Commands> = ipcMain as TypedIpcMain<Events, Commands>;
const experienceSamplingService: ExperienceSamplingService = new ExperienceSamplingService();
typedIpcMain.handle(
'createExperienceSample',
async (e, promptedAt: number, question: string, response: number): Promise<void> => {
await experienceSamplingService.createExperienceSample(promptedAt, question, response);

const LOG = getLogger('IpcHandler')
export class IpcHandler {
private readonly actions: any;
private readonly windowService: WindowService;

private readonly experienceSamplingService: ExperienceSamplingService;
private typedIpcMain: TypedIpcMain<Events, Commands> = ipcMain as TypedIpcMain<Events, Commands>;

constructor(windowService: WindowService, experienceSamplingService: ExperienceSamplingService) {
this.windowService = windowService;
this.experienceSamplingService = experienceSamplingService;
this.actions = {
createExperienceSample: this.createExperienceSample,
closeExperienceSamplingWindow: this.closeExperienceSamplingWindow
};
}

public init(): void {
Object.keys(this.actions).forEach((action: string): void => {
LOG.info(`ipcMain.handle setup: ${action}`);
ipcMain.handle(action, async (_event: IpcMainInvokeEvent, ...args): Promise<any> => {
try {
return await this.actions[action].apply(this, args);
} catch (error) {
LOG.error(error);
return error;
}
});
});
}

private async createExperienceSample(promptedAt: number, question: string, response: number) {
await this.experienceSamplingService.createExperienceSample(promptedAt, question, response);
}

private async closeExperienceSamplingWindow(): Promise<void> {
await this.windowService.closeExperienceSamplingWindow();
}
);
}
1 change: 0 additions & 1 deletion src/electron/electron/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import log from 'electron-log/main';
import { getLogger } from '../shared/Logger';
import { DatabaseService } from './services/DatabaseService';
import { SettingsService } from './services/SettingsService';
import studyConfig from '../config/study.config';
import { TrackerType } from '../enums/TrackerType.enum';
import { WindowActivityTrackerService } from './services/trackers/WindowActivityTrackerService';
import { UserInputTrackerService } from './services/trackers/UserInputTrackerService';
Expand Down
2 changes: 1 addition & 1 deletion src/electron/electron/main/services/SettingsService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Settings } from '../entities/Settings';
import studyConfig from '../../config/study.config';
import { generateAlphaNumericString } from './utils/helpers';
import studyConfig from '../../../shared/study.config';

export class SettingsService {
public async init(): Promise<void> {
Expand Down
25 changes: 18 additions & 7 deletions src/electron/electron/main/services/WindowService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ import { getLogger } from '../../shared/Logger';
import AppUpdaterService from './AppUpdaterService';
import { is } from './utils/helpers';
import path from 'path';
import studyConfig from '../../config/study.config';
import MenuItemConstructorOptions = Electron.MenuItemConstructorOptions;
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import studyConfig from '../../../shared/study.config';

const LOG = getLogger('WindowService');

export class WindowService {
private readonly appUpdaterService: AppUpdaterService;
private tray: Tray;
private readonly isDevelopment: boolean = is.dev;
private experienceSamplingWindow: BrowserWindow;

constructor(appUpdaterService: AppUpdaterService) {
this.appUpdaterService = appUpdaterService;
Expand Down Expand Up @@ -43,7 +44,7 @@ export class WindowService {
const windowWidth = 500;
const windowHeight = 130;

const win = new BrowserWindow({
this.experienceSamplingWindow = new BrowserWindow({
width: windowWidth,
height: windowHeight,
x: width - windowWidth - windowPadding,
Expand All @@ -65,21 +66,31 @@ export class WindowService {
});

if (process.env.VITE_DEV_SERVER_URL) {
await win.loadURL(process.env.VITE_DEV_SERVER_URL + '#experience-sampling');
await this.experienceSamplingWindow.loadURL(
process.env.VITE_DEV_SERVER_URL + '#experience-sampling'
);
} else {
await win.loadFile(path.join(process.env.DIST, 'index.html'), {
await this.experienceSamplingWindow.loadFile(path.join(process.env.DIST, 'index.html'), {
hash: 'experience-sampling'
});
}

win.setVisibleOnAllWorkspaces(true);
this.experienceSamplingWindow.setVisibleOnAllWorkspaces(true);
let opacity = 0;
const interval = setInterval(() => {
if (opacity >= 1) clearInterval(interval);
win?.setOpacity(opacity);
this.experienceSamplingWindow?.setOpacity(opacity);
opacity += 0.1;
}, 10);
win.show();
this.experienceSamplingWindow.show();
}

public async closeExperienceSamplingWindow() {
if (this.experienceSamplingWindow) {
this.experienceSamplingWindow.close();
this.experienceSamplingWindow.setOpacity(0);
this.experienceSamplingWindow = null;
}
}

public updateTray(
Expand Down
35 changes: 0 additions & 35 deletions src/electron/electron/shared/StudyConfig.ts

This file was deleted.

36 changes: 36 additions & 0 deletions src/electron/shared/StudyConfiguration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export interface UserInputTrackerConfiguration {
enabled: boolean;
intervalInMs: number;
}

export interface WindowActivityTrackerConfiguration {
enabled: boolean;
intervalInMs: number;
}

export interface ExperienceSamplingConfiguration {
enabled: boolean;
scale: number;
questions: string[];
responseOptions: string[][];
samplingIntervalInMinutes: number;
samplingRandomization: boolean;
}

export interface TrackerConfiguration {
windowActivityTracker: WindowActivityTrackerConfiguration;
userInputTracker: UserInputTrackerConfiguration;
experienceSampling: ExperienceSamplingConfiguration;
}

export interface StudyConfiguration {
name: string;
shortDescription: string;
infoUrl: string;
privacyPolicyUrl: string;
uploadUrl: string;
contactName: string;
contactEmail: string;
subjectIdLength: number;
trackers: TrackerConfiguration;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { StudyConfig } from '../shared/StudyConfig';
import { StudyConfiguration } from './StudyConfiguration';

const studyConfig: StudyConfig = {
const studyConfig: StudyConfiguration = {
name: 'Personal Analytics',
shortDescription: 'A study to understand how people...',
infoUrl: 'https://hasel.dev',
Expand All @@ -20,6 +20,7 @@ const studyConfig: StudyConfig = {
},
experienceSampling: {
enabled: true,
scale: 5,
questions: ['How are you feeling right now?', 'How is your day going?'],
responseOptions: [
['Bad', 'Good'],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
type Commands = {
createExperienceSample: (promptedAt: number, question: string, response: number) => Promise<void>;
closeExperienceSamplingWindow: () => Promise<void>;
};
export default Commands;
File renamed without changes.
File renamed without changes.
6 changes: 3 additions & 3 deletions src/electron/src/utils/typedIpcRenderer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TypedIpcRenderer } from '../../electron/ipc/TypedIpcMain';
import Events from '../../electron/shared/Events';
import Commands from '../../electron/shared/Commands';
import Events from './Events';
import Commands from './Commands';
import { TypedIpcRenderer } from './TypedIpcMain';

const typedIpcRenderer = window.ipcRenderer as TypedIpcRenderer<Events, Commands>;

Expand Down
45 changes: 35 additions & 10 deletions src/electron/src/views/ExperienceSampling.vue
Original file line number Diff line number Diff line change
@@ -1,32 +1,57 @@
<script lang="ts" setup></script>
<script lang="ts" setup>
import typedIpcRenderer from '../utils/typedIpcRenderer';
import studyConfig from '../../shared/study.config';
const esConfig = studyConfig.trackers.experienceSampling;
const studyQuestions = esConfig.questions;
const randomQuestionNr = Math.floor(Math.random() * studyQuestions.length);
const question = esConfig.questions[randomQuestionNr];
const questionLabels = esConfig.responseOptions[randomQuestionNr];
const scale = Array.from({ length: esConfig.scale }, (_, i) => i + 1);
const promptedAt = new Date(Date.now());
const promptedAtString = promptedAt.toLocaleTimeString().substring(0, 5);
async function createExperienceSample(value: number) {
try {
await typedIpcRenderer.invoke('createExperienceSample', promptedAt.getTime(), question, value);
await typedIpcRenderer.invoke('closeExperienceSamplingWindow');
} catch (error) {
console.error('Error creating team', error);
}
}
</script>
<template>
<div class="experience-sampling-notification">
<template v-if="true">
<div class="notification-top-bar">
<div>Self-Report: asdf</div>
<div>asdf</div>
<div>Self-Report: {{ studyConfig.name }}</div>
<div>{{ promptedAtString }}</div>
</div>
<div class="pointer-events-auto flex w-full">
<div class="w-0 flex-1 p-4 pt-1">
<div class="flex items-start">
<div class="w-0 flex-1">
<p class="prompt">asdf</p>
<p class="prompt">{{ question }}</p>
<div class="-mx-2 mt-2 flex flex-row justify-between">
<div
v-for="value in [1, 2, 3, 4, 5, 6, 7]"
v-for="value in scale"
:key="value"
class="sample-answer"
@click=""
@click="createExperienceSample(value)"
>
<span v-if="true" class="mx-auto flex font-medium">
{{ value }}
</span>
</div>
</div>
<div class="mt-1 flex flex-row text-sm text-gray-400">
<div class="">asdf</div>
<div class="mx-auto">asdf</div>
<div class="">asdf</div>
<div>{{ questionLabels[0] }}</div>
<div class="mx-auto">
<span v-if="questionLabels.length === 3">{{ questionLabels[1] }}</span>
</div>
<div>{{ questionLabels[2] || questionLabels[1] }}</div>
</div>
</div>
</div>
Expand Down Expand Up @@ -58,7 +83,7 @@
}
.sample-answer {
@apply mx-1 flex h-8 w-8 cursor-pointer items-center rounded-md border border-gray-200 bg-gray-100 hover:bg-gray-700 text-center align-middle text-gray-500 hover:text-white outline-none ring-2 ring-gray-900 transition-all;
@apply mx-1 flex h-8 w-8 cursor-pointer items-center rounded-md border border-gray-200 bg-gray-100 text-center align-middle text-gray-500 outline-none ring-2 ring-gray-900 transition-all hover:bg-gray-700 hover:text-white;
}
}
</style>

0 comments on commit 2619ba9

Please sign in to comment.