-
-
Notifications
You must be signed in to change notification settings - Fork 108
/
NewReleaseNotification.vue
123 lines (114 loc) · 4.56 KB
/
NewReleaseNotification.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
<template lang="pug">
div
b-alert(v-if="isVisible", variant="info", show)
| A new release, v{{ latestVersion }}, is available for
| #[a(href="https://activitywatch.net/downloads/" target="_blank" class="alert-link") download],
| you can also #[a(href="javascript:void(0);" class="alert-link" @click="disableCheck") disable]
| future reminders and checks for updates.
button(type="button", class="close", @click="isVisible=false") ×
b-alert(v-if="isFollowUpVisible", variant="success", show)
| Checking for new releases is now disabled, you can re-enable it in the
| #[router-link(to="/settings" class="alert-link" @click.native="isFollowUpVisible=false") settings page].
button(type="button", class="close", @click="isFollowUpVisible=false") ×
</template>
<script>
import axios from 'axios';
import moment from 'moment';
import semver from 'semver';
import { mapWritableState } from 'pinia';
import { useSettingsStore, LONG_BACKOFF_PERIOD, SHORT_BACKOFF_PERIOD } from '~/stores/settings';
import { getClient } from '~/util/awclient';
// After reminding the user every SHORT_BACKOFF_PERIOD days for BACKOFF_THRESHOLD times, switch to LONG_BACKOFF_PERIOD
const BACKOFF_THRESHOLD = 5;
export default {
name: 'new-release-notification',
data() {
return {
isVisible: false,
isFollowUpVisible: false,
currentVersion: null,
latestVersion: null,
latestVersionDate: null,
// The following constants can be used in other files, such as ReleaseNotificationSettings.vue
SHORT_BACKOFF_PERIOD: SHORT_BACKOFF_PERIOD,
LONG_BACKOFF_PERIOD: LONG_BACKOFF_PERIOD,
};
},
computed: {
...mapWritableState(useSettingsStore, { data: 'newReleaseNotification' }),
},
async mounted() {
await useSettingsStore().ensureLoaded();
if (this.data && (!this.data.isEnabled || moment() < moment(this.data.nextCheckTime))) return;
await this.retrieveCurrentVersion();
await this.retrieveLatestVersion();
this.isVisible = this.getHasNewRelease() && this.getReleaseIsReady();
if (this.isVisible && this.data) {
const _timesChecked = Math.min(this.data.timesChecked + 1, BACKOFF_THRESHOLD);
const _howOftenToCheck =
_timesChecked > BACKOFF_THRESHOLD - 1 ? LONG_BACKOFF_PERIOD : SHORT_BACKOFF_PERIOD;
this.data = {
isEnabled: true,
nextCheckTime: moment().add(_howOftenToCheck, 'seconds'),
howOftenToCheck: _howOftenToCheck,
timesChecked: _timesChecked,
};
} else {
this.data = {
isEnabled: true,
nextCheckTime: moment().add(SHORT_BACKOFF_PERIOD, 'seconds'),
howOftenToCheck: SHORT_BACKOFF_PERIOD,
timesChecked: this.isVisible ? 1 : 0,
};
}
},
methods: {
async retrieveCurrentVersion() {
try {
const response = await getClient().getInfo();
this.currentVersion = this.cleanVersionTag(response.version);
} catch (err) {
console.error('unable to connect to aw-server: ', err);
}
},
async retrieveLatestVersion() {
try {
const response = await axios.get(
'https://api.github.com/repos/ActivityWatch/activitywatch/releases/latest'
);
this.latestVersion = this.cleanVersionTag(response.data.tag_name);
this.latestVersionDate = moment(response.data.published_at);
} catch (err) {
console.error('unable to connect to GitHub API to check for latest version: ', err);
}
},
cleanVersionTag(tag) {
tag = tag.trim();
// Remove the build metadata if it exists, e.g. 'v0.8.dev+c6433ea'
const plus_idx = tag.indexOf('+');
tag = tag.substring(0, plus_idx != -1 ? plus_idx : tag.length);
// Remove server type if it exists, e.g. 'v0.8.0 (rust)'
const space_idx = tag.indexOf(' ');
tag = tag.substring(0, space_idx != -1 ? space_idx : tag.length);
return semver.valid(tag);
},
getHasNewRelease() {
// Null version means format was invalid, so fail silently and not show reminder
if (this.currentVersion && this.latestVersion)
return semver.lt(this.currentVersion, this.latestVersion);
return false;
},
getReleaseIsReady() {
// Want to make sure that the latest release is out for a week to make sure it's well tested
if (this.latestVersionDate) return moment() >= this.latestVersionDate.add(7, 'days');
return false;
},
disableCheck() {
this.isVisible = false;
this.isFollowUpVisible = true;
this.data.isEnabled = false;
this.saveData();
},
},
};
</script>