Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/components/SelectableVisualization.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ div
:namefunc="e => e.data.title",
:colorfunc="e => e.data.title",
with_limit)
div(v-if="type == 'top_bundle_ids' && activityStore.android.available")
aw-summary(:fields="activityStore.window.top_titles",
:namefunc="e => e.data.classname",
:colorfunc="e => e.data.app",
with_limit)
div(v-if="type == 'top_domains'")
aw-summary(:fields="activityStore.browser.top_domains",
:namefunc="e => e.data.$domain",
Expand Down Expand Up @@ -143,6 +148,7 @@ export default {
types: [
'top_apps',
'top_titles',
'top_bundle_ids',
'top_domains',
'top_urls',
'top_browser_titles',
Expand Down Expand Up @@ -189,7 +195,11 @@ export default {
},
top_titles: {
title: 'Top Window Titles',
available: this.activityStore.window.available,
available: this.activityStore.window.available || this.activityStore.android.available,
},
top_bundle_ids: {
title: 'Bundle IDs',
available: this.activityStore.window.available || this.activityStore.android.available,
},
top_domains: {
title: 'Top Browser Domains',
Expand Down
4 changes: 2 additions & 2 deletions src/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export function canonicalEvents(params: DesktopQueryParams | AndroidQueryParams)
// Fetch window/app events
`events = flood(query_bucket(find_bucket("${bid_window}")));`,
// On Android, merge events to avoid overload of events
isAndroidParams(params) ? 'events = merge_events_by_keys(events, ["app"]);' : '',
isAndroidParams(params) ? 'events = merge_events_by_keys(events, ["app", "title"]);' : '',
// Fetch not-afk events
isDesktopParams(params)
? `not_afk = flood(query_bucket(find_bucket("${params.bid_afk}")));
Expand Down Expand Up @@ -200,7 +200,7 @@ export function appQuery(
const code = `
${canonicalEvents(params)}

title_events = sort_by_duration(merge_events_by_keys(events, ["app", "classname"]));
title_events = sort_by_duration(merge_events_by_keys(events, ["app", "classname", "title"]));
app_events = sort_by_duration(merge_events_by_keys(title_events, ["app"]));
cat_events = sort_by_duration(merge_events_by_keys(events, ["$category"]));

Expand Down
33 changes: 33 additions & 0 deletions src/stores/activity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,39 @@ export const useActivityStore = defineStore('activity', {
filter_categories
);
const data = await getClient().query(periods, q).catch(this.errorHandler);

// Post-process for iOS compatibility (swap app <-> title)
const androidBucket = this.buckets.android[0];
const isIos = androidBucket && androidBucket.startsWith('aw-import-screentime');

if (isIos && data && data[0] && data[0].title_events) {
data[0].title_events.forEach((e: IEvent) => {
// iOS events have 'app' (bundleID) and 'title'. We swap them.
// Check if title exists to avoid overwriting with undefined
if (e.data.title) {
const originalApp = e.data.app;
e.data.classname = originalApp; // Bundle ID (e.g. com.google.ios.youtube)
e.data.app = e.data.title; // Human Name (e.g. YouTube)
}
Comment on lines +329 to +333
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Events without title are skipped but still contribute to aggregated durations from server-side query. This creates mismatch between title_events and app_events returned from query vs post-processed versions. Are there iOS events that legitimately don't have a title field, and if so should they be handled differently?

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/stores/activity.ts
Line: 329:333

Comment:
**logic:** Events without `title` are skipped but still contribute to aggregated durations from server-side query. This creates mismatch between `title_events` and `app_events` returned from query vs post-processed versions. Are there iOS events that legitimately don't have a title field, and if so should they be handled differently?

How can I resolve this? If you propose a fix, please make it concise.

});

// Re-aggregate app_events from the modified title_events
const new_app_events_map: Record<string, IEvent> = {};
data[0].title_events.forEach((e: IEvent) => {
const app = e.data.app;
if (!new_app_events_map[app]) {
// Clone event to avoid reference issues
new_app_events_map[app] = { ...e, duration: 0, data: { ...e.data } };
// Ensure we only keep the 'app' key for app_events to match standard structure
new_app_events_map[app].data = { app: app, $category: e.data.$category };
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Only preserving app and $category in re-aggregated events discards classname and title which may be needed elsewhere

Suggested change
new_app_events_map[app].data = { app: app, $category: e.data.$category };
new_app_events_map[app].data = { app: app, classname: e.data.classname, $category: e.data.$category };
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/stores/activity.ts
Line: 344:344

Comment:
**logic:** Only preserving `app` and `$category` in re-aggregated events discards `classname` and `title` which may be needed elsewhere

```suggestion
            new_app_events_map[app].data = { app: app, classname: e.data.classname, $category: e.data.$category };
```

How can I resolve this? If you propose a fix, please make it concise.

}
new_app_events_map[app].duration += e.duration;
});

// Sort by duration desc
data[0].app_events = _.orderBy(_.values(new_app_events_map), ['duration'], ['desc']);
}

this.query_window_completed(data[0]);
},

Expand Down
9 changes: 7 additions & 2 deletions src/stores/buckets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,15 @@ export const useBucketsStore = defineStore('buckets', {
);
},
bucketsAndroid(): (host: string) => string[] {
return host =>
this.bucketsByType(host, 'currentwindow').filter((id: string) =>
return host => {
const android = this.bucketsByType(host, 'currentwindow').filter((id: string) =>
id.startsWith('aw-watcher-android')
);
const ios = this.bucketsByType(host, 'app').filter((id: string) =>
id.startsWith('aw-import-screentime')
);
Comment on lines +105 to +107
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Filtering by generic type 'app' could match unintended buckets. Consider using a more specific filter or adding additional validation.

The PR description mentions these are from aw-import-screentime which should have a specific bucket type. Check actual bucket type from iOS imports to ensure correct filtering. What is the actual bucket type created by aw-import-screentime - is it truly just 'app' or something more specific?

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/stores/buckets.ts
Line: 105:107

Comment:
**logic:** Filtering by generic type `'app'` could match unintended buckets. Consider using a more specific filter or adding additional validation.

The PR description mentions these are from `aw-import-screentime` which should have a specific bucket type. Check actual bucket type from iOS imports to ensure correct filtering. What is the actual bucket type created by aw-import-screentime - is it truly just 'app' or something more specific?

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

return [...android, ...ios];
};
},
bucketsEditor(): (host: string) => string[] {
// fallback to a bucket with 'unknown' host, if one exists.
Expand Down
Loading