From 13777cbda65a1316c4e61e755f633a448602f8e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Wed, 8 Apr 2020 19:13:02 +0200 Subject: [PATCH 1/6] feat: ported new Activity view to Android --- src/components/Header.vue | 17 ++++---- src/main.js | 9 +++-- src/queries.ts | 71 +++++++++++++++++++++------------ src/store/modules/activity.ts | 55 ++++++++++++++++++++----- src/store/modules/buckets.js | 10 ++++- src/views/activity/Activity.vue | 2 +- 6 files changed, 114 insertions(+), 50 deletions(-) diff --git a/src/components/Header.vue b/src/components/Header.vue index da12744f..8263ef68 100644 --- a/src/components/Header.vue +++ b/src/components/Header.vue @@ -77,17 +77,11 @@ import 'vue-awesome/icons/desktop'; import _ from 'lodash'; -// Set this to true to test Android behavior when on a desktop -const testingAndroid = false; - export default { name: 'Header', data() { return { activityViews: [], - isAndroidApp: - testingAndroid || - (navigator.userAgent.includes('Android') && navigator.userAgent.includes('wv')), // Checks for Android and WebView }; }, mounted: async function() { @@ -98,7 +92,7 @@ export default { // The '&& true;' is just to typecoerce into booleans types_by_host[v.hostname].afk |= v.type == 'afkstatus'; types_by_host[v.hostname].window |= v.type == 'currentwindow'; - types_by_host[v.hostname].android |= v.type == 'currentwindow' && this.isAndroidApp; // Use other bucket type ID in the future + types_by_host[v.hostname].android |= v.type == 'currentwindow' && v.id.includes('android'); // Use other bucket type ID in the future }); //console.log(types_by_host); @@ -112,7 +106,7 @@ export default { icon: 'desktop', }); } - if (testingAndroid || types.android) { + if (types.android) { this.activityViews.push({ name: `${hostname} (Android)`, hostname: hostname, @@ -120,6 +114,13 @@ export default { pathUrl: '/activity/android', icon: 'mobile', }); + this.activityViews.push({ + name: `${hostname} (Android, new)`, + hostname: hostname, + type: 'android', + pathUrl: `/activity/${hostname}`, + icon: 'mobile', + }); } }); }, diff --git a/src/main.js b/src/main.js index 49c13a38..197459b5 100644 --- a/src/main.js +++ b/src/main.js @@ -23,6 +23,11 @@ import 'typeface-varela-round'; // Loads all the filters import './util/filters.js'; +// Create an instance of AWClient as this.$aw +import awclient from './util/awclient.js'; +console.log(awclient); +Vue.prototype.$aw = awclient; + // Sets up the routing and the base app (using vue-router) import router from './route.js'; @@ -54,10 +59,6 @@ Vue.component('aw-timeline-barchart', () => import('./visualizations/TimelineBar // A mixin to make async method errors propagate Vue.mixin(require('~/mixins/asyncErrorCaptured.js')); -// Create an instance of AWClient as this.$aw -import awclient from './util/awclient.js'; -Vue.prototype.$aw = awclient; - // Set the PRODUCTION constant Vue.prototype.PRODUCTION = PRODUCTION; diff --git a/src/queries.ts b/src/queries.ts index 20c319f7..add41564 100644 --- a/src/queries.ts +++ b/src/queries.ts @@ -9,18 +9,26 @@ function querystr_to_array(querystr: string): string[] { .map(l => l + ';'); } -interface QueryParams { - bid_window: string; - bid_afk: string; - bid_browsers?: string[]; +interface BaseQueryParams { filter_afk: boolean; include_audible?: boolean; classes: Record; } +interface DesktopQueryParams extends BaseQueryParams { + bid_window: string; + bid_afk: string; + bid_browsers?: string[]; +} + +interface AndroidQueryParams extends BaseQueryParams { + bid_android: string; + bid_browsers?: string[]; +} + // Constructs a query that returns a fully-detailed list of events from the merging of several sources (window, afk, web). // Puts it's results in `not_afk` and `events`. -function canonicalEvents(params: QueryParams): string { +function canonicalEvents(params: DesktopQueryParams): string { return ` events = flood(query_bucket("${params.bid_window}")); not_afk = flood(query_bucket("${params.bid_afk}")); @@ -29,18 +37,18 @@ function canonicalEvents(params: QueryParams): string { `; } +const default_limit = 100; // Hardcoded limit per group + export function windowQuery( windowbucket, afkbucket, - appcount, - titlecount, filterAFK, classes, filterCategories: string[][] ): string[] { windowbucket = windowbucket.replace('"', '\\"'); afkbucket = afkbucket.replace('"', '\\"'); - const params: QueryParams = { + const params: DesktopQueryParams = { bid_window: windowbucket, bid_afk: afkbucket, classes: classes, @@ -60,22 +68,34 @@ export function windowQuery( cat_events = sort_by_duration(merge_events_by_keys(events, ["$category"])); events = sort_by_timestamp(events); - app_events = limit_events(app_events, ${appcount}); - title_events = limit_events(title_events, ${titlecount}); + app_events = limit_events(app_events, ${default_limit}); + title_events = limit_events(title_events, ${default_limit}); duration = sum_durations(events); RETURN = {"app_events": app_events, "title_events": title_events, "cat_events": cat_events, "duration": duration, "active_events": not_afk};`; return querystr_to_array(code); } -export function appQuery(appbucket: string, limit = 5): string[] { +export function appQuery(appbucket: string, classes, filterAFK): string[] { appbucket = appbucket.replace('"', '\\"'); + const params: AndroidQueryParams = { + bid_android: appbucket, + classes: classes, + filter_afk: filterAFK, + }; const code = ` - events = query_bucket("${appbucket}"); - events = merge_events_by_keys(events, ["app"]); - events = sort_by_duration(events); - events = limit_events(events, ${limit}); - total_duration = sum_durations(events); - RETURN = {"events": events, "total_duration": total_duration}; + events = query_bucket("${params.bid_android}"); + events = merge_events_by_keys(events, ["app"]); + events = categorize(events, ${JSON.stringify(params.classes)}); + + title_events = sort_by_duration(merge_events_by_keys(events, ["app", "classname"])); + app_events = sort_by_duration(merge_events_by_keys(title_events, ["app"])); + cat_events = sort_by_duration(merge_events_by_keys(events, ["$category"])); + + events = sort_by_timestamp(events); + app_events = limit_events(app_events, ${default_limit}); + title_events = limit_events(title_events, ${default_limit}); + duration = sum_durations(events); + RETURN = {"app_events": app_events, "title_events": title_events, "cat_events": cat_events, "duration": duration, "active_events": app_events}; `; return querystr_to_array(code); } @@ -122,7 +142,7 @@ function browsersWithBuckets(browserbuckets: string[]): [string, string][] { } // Returns a list of active browser events (where the browser was the active window) from all browser buckets -function browserEvents(params: QueryParams): string { +function browserEvents(params: DesktopQueryParams): string { // If multiple browser buckets were found // AFK filtered later in the process let code = ` @@ -151,7 +171,6 @@ export function browserSummaryQuery( browserbuckets: string[], windowbucket: string, afkbucket: string, - limit = 5, filterAFK = true ): string[] { // Escape `"` @@ -160,7 +179,7 @@ export function browserSummaryQuery( afkbucket = afkbucket.replace('"', '\\"'); // TODO: Get classes - const params: QueryParams = { + const params: DesktopQueryParams = { bid_window: windowbucket, bid_afk: afkbucket, bid_browsers: browserbuckets, @@ -172,17 +191,17 @@ export function browserSummaryQuery( `${browserEvents(params)} urls = merge_events_by_keys(events, ["url"]); urls = sort_by_duration(urls); - urls = limit_events(urls, ${limit}); + urls = limit_events(urls, ${default_limit}); domains = split_url_events(events); domains = merge_events_by_keys(domains, ["$domain"]); domains = sort_by_duration(domains); - domains = limit_events(domains, ${limit}); + domains = limit_events(domains, ${default_limit}); duration = sum_durations(events); RETURN = {"domains": domains, "urls": urls, "duration": duration};` ); } -export function editorActivityQuery(editorbuckets: string[], limit): string[] { +export function editorActivityQuery(editorbuckets: string[]): string[] { let q = ['events = [];']; for (let editorbucket of editorbuckets) { editorbucket = editorbucket.replace('"', '\\"'); @@ -190,11 +209,11 @@ export function editorActivityQuery(editorbuckets: string[], limit): string[] { } q = q.concat([ 'files = sort_by_duration(merge_events_by_keys(events, ["file", "language"]));', - 'files = limit_events(files, ' + limit + ');', + `files = limit_events(files, ${default_limit});`, 'languages = sort_by_duration(merge_events_by_keys(events, ["language"]));', - 'languages = limit_events(languages, ' + limit + ');', + `languages = limit_events(languages, ${default_limit});`, 'projects = sort_by_duration(merge_events_by_keys(events, ["project"]));', - 'projects = limit_events(projects, ' + limit + ');', + `projects = limit_events(projects, ${default_limit});`, 'duration = sum_durations(events);', 'RETURN = {"files": files, "languages": languages, "projects": projects, "duration": duration};', ]); diff --git a/src/store/modules/activity.ts b/src/store/modules/activity.ts index 2c4af994..e0faa8e6 100644 --- a/src/store/modules/activity.ts +++ b/src/store/modules/activity.ts @@ -6,8 +6,6 @@ import queries from '~/queries'; import { loadClassesForQuery } from '~/util/classes'; import { get_day_start_with_offset } from '~/util/time'; -const default_limit = 100; - interface TimePeriod { start: string; length: [number, string]; @@ -69,10 +67,15 @@ const _state = { history: {}, }, + android: { + available: false, + }, + query_options: { browser_buckets: 'all', editor_buckets: 'all', }, + buckets: { afk_buckets: [], window_buckets: [], @@ -130,8 +133,12 @@ const actions = { if (state.window.available) { await dispatch('query_window', query_options); + } else if (state.android.available) { + await dispatch('query_android', query_options); } else { - console.log('Cannot call query_window as we are missing either an afk or window bucket'); + console.log( + 'Cannot query windows as we are missing either an afk/window bucket pair or an android bucket' + ); await dispatch('query_window_empty', query_options); } @@ -146,6 +153,8 @@ const actions = { if (state.active.available) { await dispatch('query_active_history', query_options); + } else if (state.android.available) { + await dispatch('query_active_history_android', query_options); } else { console.log('Cannot call query_active_history as we do not have an afk bucket'); await dispatch('query_active_history_empty', query_options); @@ -166,18 +175,26 @@ const actions = { async query_window({ state, commit }, { timeperiod, filterAFK, filterCategories }: QueryOptions) { const periods = [timeperiodToStr(timeperiod)]; + const start = moment(); const classes = loadClassesForQuery(); const q = queries.windowQuery( state.buckets.window_buckets[0], state.buckets.afk_buckets[0], - default_limit, // this.top_apps_count, - default_limit, // this.top_windowtitles_count, filterAFK, classes, filterCategories ); const data = await this._vm.$aw.query(periods, q); commit('query_window_completed', data[0]); + console.info(`Completed window query in ${moment().diff(start)}ms`); + }, + + async query_android({ state, commit }, { timeperiod, filterCategories }: QueryOptions) { + const periods = [timeperiodToStr(timeperiod)]; + const classes = loadClassesForQuery(); + const q = queries.appQuery(state.buckets.window_buckets[0], classes, filterCategories); + const data = await this._vm.$aw.query(periods, q).catch(this.errorHandler); + commit('query_window_completed', data[0]); }, async query_window_empty({ commit }) { @@ -197,7 +214,6 @@ const actions = { state.buckets.browser_buckets, state.buckets.window_buckets[0], state.buckets.afk_buckets[0], - default_limit, // this.top_web_count filterAFK ); const data = await this._vm.$aw.query(periods, q); @@ -215,7 +231,7 @@ const actions = { async query_editor({ state, commit }, { timeperiod }) { const periods = [timeperiodToStr(timeperiod)]; - const q = queries.editorActivityQuery(state.buckets.editor_buckets, 100); + const q = queries.editorActivityQuery(state.buckets.editor_buckets); const data = await this._vm.$aw.query(periods, q); commit('query_editor_completed', data[0]); }, @@ -233,12 +249,30 @@ const actions = { const periods = timeperiodStrsAroundTimeperiod(timeperiod).filter(tp_str => { return !_.includes(state.active.history, tp_str); }); - const bucket_id_afk = state.buckets.afk_buckets[0]; - const data = await this._vm.$aw.query(periods, queries.dailyActivityQuery(bucket_id_afk)); + const data = await this._vm.$aw.query( + periods, + queries.dailyActivityQuery(state.buckets.afk_buckets[0]) + ); const active_history = _.zipObject( periods, _.map(data, pair => _.filter(pair, e => e.data.status == 'not-afk')) ); + console.log(active_history); + commit('query_active_history_completed', { active_history }); + }, + + async query_active_history_android({ commit, state }, { timeperiod }: QueryOptions) { + const periods = timeperiodStrsAroundTimeperiod(timeperiod).filter(tp_str => { + return !_.includes(state.active.history, tp_str); + }); + const data = await this._vm.$aw.query( + periods, + queries.dailyActivityQueryAndroid(state.buckets.window_buckets[0]) + ); + let active_history = _.zipObject(periods, data); + active_history = _.mapValues(active_history, (duration, key) => { + return [{ timestamp: key.split('/')[0], duration, data: { status: 'not-afk' } }]; + }); commit('query_active_history_completed', { active_history }); }, @@ -256,11 +290,13 @@ const actions = { state.buckets.browser_buckets.length > 0; const active_available = state.buckets.afk_buckets.length > 0; const editor_available = state.buckets.editor_buckets.length > 0; + const android_available = state.buckets.window_buckets[0].includes('android'); commit('set_available', { window_available: window_available, browser_available: browser_available, active_available: active_available, editor_available: editor_available, + android_available: android_available, }); }, @@ -467,6 +503,7 @@ const mutations = { state.active.available = data['active_available']; state.editor.available = data['editor_available']; state.category.available = data['window_available']; + state.android.available = data['android_available']; }, query_window_completed(state, data) { diff --git a/src/store/modules/buckets.js b/src/store/modules/buckets.js index bc85563a..e6c53568 100644 --- a/src/store/modules/buckets.js +++ b/src/store/modules/buckets.js @@ -3,16 +3,22 @@ const state = { buckets: [], }; +// TODO: Remove the bucket['id'].includes(testing) check (only needed because I had an old bucket laying around) function get_buckets_by_type(buckets, type) { return _.map( - _.filter(buckets, bucket => bucket['type'] === type), + _.filter(buckets, bucket => bucket['type'] === type && !bucket['id'].includes('testing')), bucket => bucket['id'] ); } +// TODO: Remove the bucket['id'].includes(testing) check (only needed because I had an old bucket laying around) function get_buckets_by_host_and_type(buckets, host, type) { return _.map( - _.filter(buckets, bucket => bucket['type'] === type && bucket['hostname'] == host), + _.filter( + buckets, + bucket => + bucket['type'] === type && bucket['hostname'] == host && !bucket['id'].includes('testing') + ), bucket => bucket['id'] ); } diff --git a/src/views/activity/Activity.vue b/src/views/activity/Activity.vue index beb15f05..3af66d12 100644 --- a/src/views/activity/Activity.vue +++ b/src/views/activity/Activity.vue @@ -25,7 +25,7 @@ div @change="(periodLength) => setDate(date, periodLength)") div.p-1(v-if="periodLength === 'day'") input.form-control(id="date" type="date" :value="date" :max="today" - @change="setDate($event.target.value)") + @change="setDate($event.target.value, periodLength)") div.p-1.ml-auto b-button-group From f90c0af017795711e04c5fd56f0628fd06f17098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Tue, 28 Apr 2020 16:10:55 +0200 Subject: [PATCH 2/6] refactor: changed buckets.(type)_buckets to simply buckets.(type), added androidBucketsByHost --- src/store/modules/activity.ts | 51 ++++++++++++++++++----------------- src/store/modules/buckets.js | 16 ++++++----- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/store/modules/activity.ts b/src/store/modules/activity.ts index e0faa8e6..f262f23d 100644 --- a/src/store/modules/activity.ts +++ b/src/store/modules/activity.ts @@ -77,10 +77,11 @@ const _state = { }, buckets: { - afk_buckets: [], - window_buckets: [], - editor_buckets: [], - browser_buckets: [], + afk: [], + window: [], + editor: [], + browser: [], + android: [], }, }; @@ -178,8 +179,8 @@ const actions = { const start = moment(); const classes = loadClassesForQuery(); const q = queries.windowQuery( - state.buckets.window_buckets[0], - state.buckets.afk_buckets[0], + state.buckets.window[0], + state.buckets.afk[0], filterAFK, classes, filterCategories @@ -192,7 +193,7 @@ const actions = { async query_android({ state, commit }, { timeperiod, filterCategories }: QueryOptions) { const periods = [timeperiodToStr(timeperiod)]; const classes = loadClassesForQuery(); - const q = queries.appQuery(state.buckets.window_buckets[0], classes, filterCategories); + const q = queries.appQuery(state.buckets.window[0], classes, filterCategories); const data = await this._vm.$aw.query(periods, q).catch(this.errorHandler); commit('query_window_completed', data[0]); }, @@ -211,9 +212,9 @@ const actions = { async query_browser({ state, commit }, { timeperiod, filterAFK }: QueryOptions) { const periods = [timeperiodToStr(timeperiod)]; const q = queries.browserSummaryQuery( - state.buckets.browser_buckets, - state.buckets.window_buckets[0], - state.buckets.afk_buckets[0], + state.buckets.browser, + state.buckets.window[0], + state.buckets.afk[0], filterAFK ); const data = await this._vm.$aw.query(periods, q); @@ -231,7 +232,7 @@ const actions = { async query_editor({ state, commit }, { timeperiod }) { const periods = [timeperiodToStr(timeperiod)]; - const q = queries.editorActivityQuery(state.buckets.editor_buckets); + const q = queries.editorActivityQuery(state.buckets.editor); const data = await this._vm.$aw.query(periods, q); commit('query_editor_completed', data[0]); }, @@ -251,7 +252,7 @@ const actions = { }); const data = await this._vm.$aw.query( periods, - queries.dailyActivityQuery(state.buckets.afk_buckets[0]) + queries.dailyActivityQuery(state.buckets.afk[0]) ); const active_history = _.zipObject( periods, @@ -267,7 +268,7 @@ const actions = { }); const data = await this._vm.$aw.query( periods, - queries.dailyActivityQueryAndroid(state.buckets.window_buckets[0]) + queries.dailyActivityQueryAndroid(state.buckets.window[0]) ); let active_history = _.zipObject(periods, data); active_history = _.mapValues(active_history, (duration, key) => { @@ -282,15 +283,14 @@ const actions = { }, async set_available({ commit, state }) { - const window_available = - state.buckets.afk_buckets.length > 0 && state.buckets.window_buckets.length > 0; + const window_available = state.buckets.afk.length > 0 && state.buckets.window.length > 0; const browser_available = - state.buckets.afk_buckets.length > 0 && - state.buckets.window_buckets.length > 0 && - state.buckets.browser_buckets.length > 0; - const active_available = state.buckets.afk_buckets.length > 0; - const editor_available = state.buckets.editor_buckets.length > 0; - const android_available = state.buckets.window_buckets[0].includes('android'); + state.buckets.afk.length > 0 && + state.buckets.window.length > 0 && + state.buckets.browser.length > 0; + const active_available = state.buckets.afk.length > 0; + const editor_available = state.buckets.editor.length > 0; + const android_available = state.buckets.android.length > 0; commit('set_available', { window_available: window_available, browser_available: browser_available, @@ -302,10 +302,11 @@ const actions = { async get_buckets({ commit, rootGetters }, { host }) { const buckets = { - afk_buckets: rootGetters['buckets/afkBucketsByHost'](host), - window_buckets: rootGetters['buckets/windowBucketsByHost'](host), - browser_buckets: rootGetters['buckets/browserBuckets'], - editor_buckets: rootGetters['buckets/editorBuckets'], + afk: rootGetters['buckets/afkBucketsByHost'](host), + window: rootGetters['buckets/windowBucketsByHost'](host), + android: rootGetters['buckets/androidBucketsByHost'](host), + browser: rootGetters['buckets/browserBuckets'], + editor: rootGetters['buckets/editorBuckets'], }; console.log('Available buckets: ', buckets); commit('buckets', buckets); diff --git a/src/store/modules/buckets.js b/src/store/modules/buckets.js index e6c53568..99e430e1 100644 --- a/src/store/modules/buckets.js +++ b/src/store/modules/buckets.js @@ -25,17 +25,19 @@ function get_buckets_by_host_and_type(buckets, host, type) { // getters const getters = { - afkBuckets(state) { - return get_buckets_by_type(state.buckets, 'afkstatus'); - }, afkBucketsByHost: state => host => { return get_buckets_by_host_and_type(state.buckets, host, 'afkstatus'); }, - windowBuckets(state) { - return get_buckets_by_type(state.buckets, 'currentwindow'); - }, windowBucketsByHost: state => host => { - return get_buckets_by_host_and_type(state.buckets, host, 'currentwindow'); + return _.filter( + get_buckets_by_host_and_type(state.buckets, host, 'currentwindow'), + id => !id.startsWith('aw-watcher-android') + ); + }, + androidBucketsByHost: state => host => { + return _.filter(get_buckets_by_host_and_type(state.buckets, host, 'currentwindow'), id => + id.startsWith('aw-watcher-android') + ); }, editorBuckets(state) { return get_buckets_by_type(state.buckets, 'app.editor.activity'); From 035d4cc66c4a2560133aab3afa8cb6d5427a1d44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Tue, 28 Apr 2020 16:11:15 +0200 Subject: [PATCH 3/6] fix: readded active history caching --- src/store/modules/activity.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/store/modules/activity.ts b/src/store/modules/activity.ts index f262f23d..9fd0ad26 100644 --- a/src/store/modules/activity.ts +++ b/src/store/modules/activity.ts @@ -495,7 +495,12 @@ const mutations = { state.active.duration = null; state.active.events = null; - state.active.history = {}; + // Ensures that active history isn't being fully reloaded on every date change + // (see caching done in query_active_history and query_active_history_android) + // FIXME: Better detection of when to actually clear (such as on force reload, hostname change) + if (Object.keys(state.active.history).length === 0) { + state.active.history = {}; + } }, set_available(state, data) { From afdf0fc0766224eef16510471081c66b8d369057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Tue, 28 Apr 2020 16:21:52 +0200 Subject: [PATCH 4/6] refactor: removed old Android activity view --- src/components/Header.vue | 9 +- src/route.js | 3 - src/views/activity/ActivityAndroid.vue | 165 ------------------------- 3 files changed, 2 insertions(+), 175 deletions(-) delete mode 100644 src/views/activity/ActivityAndroid.vue diff --git a/src/components/Header.vue b/src/components/Header.vue index 8263ef68..f182dbaf 100644 --- a/src/components/Header.vue +++ b/src/components/Header.vue @@ -87,6 +87,8 @@ export default { mounted: async function() { const buckets = await this.$aw.getBuckets(); const types_by_host = {}; + + // TODO: Change to use same bucket detection logic as get_buckets/set_available in store/modules/activity.ts _.each(buckets, v => { types_by_host[v.hostname] = types_by_host[v.hostname] || {}; // The '&& true;' is just to typecoerce into booleans @@ -111,13 +113,6 @@ export default { name: `${hostname} (Android)`, hostname: hostname, type: 'android', - pathUrl: '/activity/android', - icon: 'mobile', - }); - this.activityViews.push({ - name: `${hostname} (Android, new)`, - hostname: hostname, - type: 'android', pathUrl: `/activity/${hostname}`, icon: 'mobile', }); diff --git a/src/route.js b/src/route.js index 559fef7f..4bbf888b 100644 --- a/src/route.js +++ b/src/route.js @@ -9,7 +9,6 @@ const ActivitySummary = () => import('./views/activity/ActivitySummary.vue'); const ActivityWindow = () => import('./views/activity/ActivityWindow.vue'); const ActivityBrowser = () => import('./views/activity/ActivityBrowser.vue'); const ActivityEditor = () => import('./views/activity/ActivityEditor.vue'); -const ActivityAndroid = () => import('./views/activity/ActivityAndroid.vue'); const Buckets = () => import('./views/Buckets.vue'); const Bucket = () => import('./views/Bucket.vue'); @@ -25,8 +24,6 @@ Vue.use(VueRouter); const router = new VueRouter({ routes: [ { path: '/', component: Home }, - // Must be before general activity view such that it matches first - { path: '/activity/android/:host/:date?', component: ActivityAndroid, props: true }, { path: '/activity/:host/:periodLength?/:date?', component: Activity, diff --git a/src/views/activity/ActivityAndroid.vue b/src/views/activity/ActivityAndroid.vue deleted file mode 100644 index fa896500..00000000 --- a/src/views/activity/ActivityAndroid.vue +++ /dev/null @@ -1,165 +0,0 @@ - - - - - From 83d14ca700e8aa5e33c0b68566f7ee6e27c85879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Tue, 28 Apr 2020 16:22:28 +0200 Subject: [PATCH 5/6] fix: fixed use of wrong bucket --- src/store/modules/activity.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/store/modules/activity.ts b/src/store/modules/activity.ts index 9fd0ad26..84f6af04 100644 --- a/src/store/modules/activity.ts +++ b/src/store/modules/activity.ts @@ -193,7 +193,7 @@ const actions = { async query_android({ state, commit }, { timeperiod, filterCategories }: QueryOptions) { const periods = [timeperiodToStr(timeperiod)]; const classes = loadClassesForQuery(); - const q = queries.appQuery(state.buckets.window[0], classes, filterCategories); + const q = queries.appQuery(state.buckets.android[0], classes, filterCategories); const data = await this._vm.$aw.query(periods, q).catch(this.errorHandler); commit('query_window_completed', data[0]); }, @@ -268,7 +268,7 @@ const actions = { }); const data = await this._vm.$aw.query( periods, - queries.dailyActivityQueryAndroid(state.buckets.window[0]) + queries.dailyActivityQueryAndroid(state.buckets.android[0]) ); let active_history = _.zipObject(periods, data); active_history = _.mapValues(active_history, (duration, key) => { From b215d5086f9e0bddff724570e435a641af1669a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Tue, 28 Apr 2020 16:57:45 +0200 Subject: [PATCH 6/6] refactor: cleanup and minor restyling of SelectableVisualization --- src/components/SelectableVisualization.vue | 153 +++++++++------------ src/store/modules/activity.ts | 2 +- 2 files changed, 69 insertions(+), 86 deletions(-) diff --git a/src/components/SelectableVisualization.vue b/src/components/SelectableVisualization.vue index 182d5e4e..9ea8e056 100644 --- a/src/components/SelectableVisualization.vue +++ b/src/components/SelectableVisualization.vue @@ -1,23 +1,30 @@ - @@ -109,24 +113,56 @@ export default { }; }, computed: { - top_apps: function() { - return this.$store.state.activity.window.top_apps; - }, - top_titles: function() { - return this.$store.state.activity.window.top_titles; - }, - top_domains: function() { - return this.$store.state.activity.browser.top_domains; - }, - top_urls: function() { - return this.$store.state.activity.browser.top_urls; - }, - top_categories: function() { - return this.$store.state.activity.category.top; + visualizations: function() { + return { + top_apps: { + title: 'Top Applications', + available: + this.$store.state.activity.window.available || + this.$store.state.activity.android.available, + }, + top_titles: { + title: 'Top Window Titles', + available: this.$store.state.activity.window.available, + }, + top_domains: { + title: 'Top Browser Domains', + available: this.$store.state.activity.browser.available, + }, + top_urls: { + title: 'Top Browser URLs', + available: this.$store.state.activity.browser.available, + }, + top_editor_files: { + title: 'Top Editor Files', + available: this.$store.state.activity.editor.available, + }, + top_editor_languages: { + title: 'Top Editor Languages', + available: this.$store.state.activity.editor.available, + }, + top_editor_projects: { + title: 'Top Editor Projects', + available: this.$store.state.activity.editor.available, + }, + top_categories: { + title: 'Top Categories', + available: this.$store.state.activity.category.available, + }, + category_tree: { + title: 'Category Tree', + available: this.$store.state.activity.category.available, + }, + category_sunburst: { + title: 'Category Sunburst', + available: this.$store.state.activity.category.available, + }, + }; }, top_categories_hierarchy: function() { - if (this.top_categories) { - const categories = this.top_categories.map(c => { + const top_categories = this.$store.state.activity.category.top; + if (top_categories) { + const categories = top_categories.map(c => { return { name: c.data.$category, size: c.duration }; }); @@ -138,59 +174,6 @@ export default { return null; } }, - type_title: function() { - return this.get_type_title(this.type); - }, - }, - methods: { - get_type_available: function(type) { - if (type === 'top_apps' || type === 'top_titles') { - return this.$store.state.activity.window.available; - } else if (type === 'top_domains' || type === 'top_urls') { - return this.$store.state.activity.browser.available; - } else if ( - type === 'top_editor_files' || - type === 'top_editor_languages' || - type === 'top_editor_projects' - ) { - return this.$store.state.activity.editor.available; - } else if ( - type === 'top_categories' || - type === 'category_tree' || - type === 'category_sunburst' - ) { - return this.$store.state.activity.category.available; - } else { - console.error('Unknown type available: ', type); - return false; - } - }, - get_type_title: function(type) { - if (type === 'top_apps') { - return 'Top Applications'; - } else if (type === 'top_titles') { - return 'Top Window Titles'; - } else if (type === 'top_domains') { - return 'Top Browser Domains'; - } else if (type === 'top_urls') { - return 'Top Browser URLs'; - } else if (type === 'top_editor_files') { - return 'Top Editor Files'; - } else if (type === 'top_editor_languages') { - return 'Top Editor Languages'; - } else if (type === 'top_editor_projects') { - return 'Top Editor Projects'; - } else if (type === 'top_categories') { - return 'Top Categories'; - } else if (type === 'category_tree') { - return 'Category Tree'; - } else if (type === 'category_sunburst') { - return 'Category Sunburst'; - } else { - console.error('Unknown type: ', type); - return 'Unknown'; - } - }, }, }; diff --git a/src/store/modules/activity.ts b/src/store/modules/activity.ts index 84f6af04..13330177 100644 --- a/src/store/modules/activity.ts +++ b/src/store/modules/activity.ts @@ -508,7 +508,7 @@ const mutations = { state.browser.available = data['browser_available']; state.active.available = data['active_available']; state.editor.available = data['editor_available']; - state.category.available = data['window_available']; + state.category.available = data['window_available'] || data['android_available']; state.android.available = data['android_available']; },