Skip to content

Commit 33d326b

Browse files
committed
Backup/restore data
1 parent a47b990 commit 33d326b

File tree

10 files changed

+108
-2
lines changed

10 files changed

+108
-2
lines changed

src/_locales/de/messages.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,5 +248,15 @@
248248
},
249249
"cancel": {
250250
"message": "Abbrechen"
251+
},
252+
"backupAndRestore": {
253+
"message": "Sicherung und Wiederherstellen",
254+
"description": "Sie können eine Sicherungskopie aller Daten für alle besuchten Websites herunterladen"
255+
},
256+
"backup": {
257+
"message": "Sicherung"
258+
},
259+
"restore": {
260+
"message": "Wiederherstellen"
251261
}
252262
}

src/_locales/en/messages.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,5 +248,15 @@
248248
},
249249
"cancel": {
250250
"message": "Cancel"
251+
},
252+
"backupAndRestore": {
253+
"message": "Backup and restore",
254+
"description": "You can download a backup copy of all data for all visited sites"
255+
},
256+
"backup": {
257+
"message": "Backup"
258+
},
259+
"restore": {
260+
"message": "Restore"
251261
}
252262
}

src/_locales/ru/messages.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,5 +248,15 @@
248248
},
249249
"cancel": {
250250
"message": "Отмена"
251+
},
252+
"backupAndRestore": {
253+
"message": "Резервное копирование и восстановление",
254+
"description": "Вы можете скачать резервную копию всех данных по всем посещенным сайтам"
255+
},
256+
"backup": {
257+
"message": "Скачать резервную копию"
258+
},
259+
"restore": {
260+
"message": "Восстановить из резервной копиии"
251261
}
252262
}

src/components/GeneralSettings.vue

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,19 @@
8686
<p class="description">{{ t('removeAllData.description') }}</p>
8787
<input type="button" :value="t('remove.message')" @click="removeAll()" />
8888
</div>
89+
<div class="settings-item">
90+
<label class="setting-header d-inline-block">{{ t('backupAndRestore.message') }}</label>
91+
<p class="description">{{ t('backupAndRestore.description') }}</p>
92+
<input type="button" :value="t('backup.message')" @click="backup()" />
93+
<input
94+
type="file"
95+
ref="restoreFile"
96+
style="display: none"
97+
@change="restoreFileUpload()"
98+
accept="application/json"
99+
/>
100+
<input type="button" class="ml-10" :value="t('restore.message')" @click="restore()" />
101+
</div>
89102
<div id="removeAllConfirmModal" class="modal" v-if="needToConfirmDeleteAllData">
90103
<div class="modal-content">
91104
<p class="text-center">{{ t('removeAllDataConfirm.message') }}</p>
@@ -121,10 +134,12 @@ import {
121134
VIEW_TIME_IN_BADGE_DEFAULT,
122135
InactivityInterval,
123136
} from '../storage/storage-params';
124-
import { ranges, ThisWeekRange } from '../utils/date';
137+
import { ranges, ThisWeekRange, todayLocalDate } from '../utils/date';
125138
import { useImportToCsv } from '../compositions/toCsv';
126139
import { FileType, useFile } from '../compositions/loadFile';
127140
import { removeAllData } from '../compositions/remove-all-data';
141+
import { injectTabsRepository } from '../repository/inject-tabs-repository';
142+
import { restoreData } from '../compositions/restoreData';
128143
129144
const { t } = useI18n();
130145
@@ -141,6 +156,8 @@ const presetRanges = ranges();
141156
142157
const needToConfirmDeleteAllData = ref<boolean>();
143158
159+
const restoreFile = ref<any>();
160+
144161
onMounted(async () => {
145162
viewTimeInBadge.value = await settingsStorage.getValue(
146163
StorageParams.VIEW_TIME_IN_BADGE,
@@ -204,6 +221,45 @@ async function removeAllConfirm() {
204221
function cancel() {
205222
needToConfirmDeleteAllData.value = false;
206223
}
224+
225+
async function backup() {
226+
const repo = await injectTabsRepository();
227+
const tabs = repo.getTabs();
228+
const json = JSON.stringify(tabs);
229+
useFile(json, FileType.JSON, `backup-${todayLocalDate()}.json`);
230+
}
231+
232+
function restore() {
233+
restoreFile.value.click();
234+
}
235+
236+
function restoreFileUpload() {
237+
try {
238+
const file = restoreFile.value.files[0];
239+
if (file != null && file.type === FileType.JSON) {
240+
var reader = new FileReader();
241+
reader.readAsText(file, 'UTF-8');
242+
reader.onload = async readerEvent => {
243+
if (readerEvent != null) {
244+
let content = readerEvent.target?.result;
245+
if (content != null) {
246+
await restoreData(content as string);
247+
}
248+
}
249+
};
250+
} else {
251+
notification.notify({
252+
title: 'Wrong restore file format',
253+
type: 'warn',
254+
});
255+
}
256+
} catch {
257+
notification.notify({
258+
title: 'Wrong restore file format',
259+
type: 'warn',
260+
});
261+
}
262+
}
207263
</script>
208264

209265
<style scoped>

src/components/TimeIntervalChart.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ function convertTimIntervalToObject(
9292
});
9393
return arr;
9494
}
95-
return null;
9695
}
9796
9897
function fillData(timeIntervalList: TimeInterval[]) {

src/compositions/loadFile.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export enum FileType {
22
CSV = 'text/csv',
3+
JSON = 'application/json',
34
}
45

56
export function useFile(data: string, type: FileType, fileName: string) {

src/compositions/restoreData.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import Browser from 'webextension-polyfill';
2+
import { Messages } from '../utils/messages';
3+
4+
export async function restoreData(json: string) {
5+
if (json != null && json != undefined && json != '') {
6+
const data = JSON.parse(json);
7+
await Browser.runtime.sendMessage({
8+
message: Messages.Restore,
9+
data: data,
10+
});
11+
}
12+
}

src/repository/tabs-repository-interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Tab } from '../entity/tab';
22

33
export interface ITabsRepository {
4+
initAsync(): void;
45
getTabs(): Tab[];
56
removeAllTabs(): void;
67
getTodayTabs(): Tab[];

src/tracker.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,4 +169,10 @@ Browser.runtime.onMessage.addListener(async message => {
169169
repo.removeAllTabs();
170170
await storage.saveTabs([]);
171171
}
172+
if (message.message == Messages.Restore) {
173+
const storage = injecStorage();
174+
await storage.saveTabs(message.data);
175+
const repo = await injectTabsRepositorySingleton();
176+
repo.initAsync();
177+
}
172178
});

src/utils/messages.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export enum Messages {
22
RescheduleJobs = 'reschedule-jobs',
33
ClearAllData = 'clear-data',
4+
Restore = 'restore-data',
45
}

0 commit comments

Comments
 (0)