Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add ability to restore backups without accessing filesystem #513

Merged
merged 3 commits into from
Jun 3, 2024
Merged
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
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ android {
applicationId "com.moimob.drinkable"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 14900
versionName "1.49.0"
versionCode 15000
versionName "1.50.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
Expand Down
1 change: 1 addition & 0 deletions fastlane/metadata/android/en-US/changelogs/15000.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
• Added ability to restore backup without accessing files in the filesystem
5 changes: 4 additions & 1 deletion src/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -182,5 +182,8 @@
"create-backup": "Create Backup",
"backup-restore-warning-text": "Are you sure you want to restore this backup? This will overwrite your current data.",
"backup-restored-successfully": "Backup restored successfully",
"backup-name-placeholder": "Backup {{date}}"
"backup-name-placeholder": "Backup {{date}}",
"paste-backup-content-title": "Paste Backup Content",
"paste-backup-content": "Paste the content of your backup file here to restore it",
"android-storage-limitations": "Due to Android 11 storage limitations, backups cannot be loaded if the app was reinstalled or backup was modified externally"
}
28 changes: 26 additions & 2 deletions src/modules/user/backups/backups.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,31 @@
<template>
<div class="p-4 pt-0">
<div class="flex justify-end items-center mb-3">
<button click.delegate="openDialog()" class="btn btn-secondary" t="create" data-cy="create-backup"></button>
<div role="alert" class="alert alert-warning">
<svg
xmlns="http://www.w3.org/2000/svg"
class="stroke-current shrink-0 h-6 w-6"
fill="none"
viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
<span class="text-sm" t="android-storage-limitations"> </span>
</div>

<div class="flex justify-between items-center my-6">
<button
click.delegate="openLoadBackupDialog()"
class="btn"
t="restore-backup"
data-cy="load-backup"></button>
<button
click.delegate="openDialog()"
class="btn btn-secondary"
t="create-backup"
data-cy="create-backup"></button>
</div>
<div data-cy="backups-container">
<div class="text-sm text-error font-semibold">${errorMessage}</div>
Expand Down
7 changes: 7 additions & 0 deletions src/modules/user/backups/backups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { LocalStorageService } from 'services/local-storage-service';
import { BackupDrawer } from './backup-drawer';
import { drinkableBackupsPath } from './constants';
import { RestoreBackupDialog } from './restore-backup-dialog';
import { LoadBackupDialog } from './load-backup-dialog';

@autoinject
export class Backups {
Expand Down Expand Up @@ -82,6 +83,12 @@ export class Backups {
});
}

openLoadBackupDialog() {
this._dialogService.open({ viewModel: LoadBackupDialog, model: null, lock: true }).whenClosed(async () => {
await this.attached();
});
}

restoreBackup(backup: BackupFileMetadata) {
this._dialogService
.open({
Expand Down
26 changes: 26 additions & 0 deletions src/modules/user/backups/load-backup-dialog.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<template>
<ux-dialog class="small-dialog" data-cy="manage-cocktail-row-dialog">
<div click.delegate="cancel()">
<div click.delegate="$event.stopPropagation()" class="bg-base-100 modal-box relative m-auto p-4">
<label class="btn btn-sm btn-circle absolute right-2 top-2" click.trigger="cancel()">✕</label>
<h3 class="text-lg font-bold" t="paste-backup-content-title"></h3>

<div class="divider mt-2"></div>

<p class="text-sm font-semibold mb-2" t="paste-backup-content"></p>

<textarea class="textarea textarea-bordered w-full" value.bind="backupJson" rows="10"></textarea>

<div class="text-sm text-error font-semibold">${errorMessage}</div>

<div class="flex w-full justify-end mt-2">
<button
click.delegate="restoreBackup()"
disabled.bind="isValid === false"
class="btn btn-warning"
t="restore-backup"></button>
</div>
</div>
</div>
</ux-dialog>
</template>
64 changes: 64 additions & 0 deletions src/modules/user/backups/load-backup-dialog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { DialogController } from 'aurelia-dialog';
import { autoinject, observable } from 'aurelia-framework';
import { I18N } from 'aurelia-i18n';
import { ToastService } from 'components/toast/toast-service';
import { CocktailService } from 'services/cocktail-service';
import { IngredientService } from 'services/ingredient-service';
import { LocalStorageService } from 'services/local-storage-service';

@autoinject()
export class LoadBackupDialog {
public controller: DialogController;
@observable public backupJson: string;
public isValid = false;
errorMessage: string;

constructor(
private _dialogContoller: DialogController,
private _localStorageService: LocalStorageService,
private _ingredientService: IngredientService,
private _cocktailService: CocktailService,
private _toastService: ToastService,
private _i18n: I18N
) {}

backupJsonChanged(newValue: string) {
this.isValid = this.IsValidJsonString(newValue.trim());
}

private IsValidJsonString(str: string): boolean {
try {
const json = JSON.parse(str);
console.log(json);
return typeof json === 'object';
} catch (e) {
console.log(e);
return false;
}
}

async restoreBackup() {
try {
this.errorMessage = '';
const backup = JSON.parse(this.backupJson);

await this._localStorageService.restoreBackup(backup);
this._ingredientService.reloadService();
this._cocktailService.reloadService();

this._toastService.addToastElement({
className: 'alert-success',
text: this._i18n.tr('backup-restored-successfully')
});

this._dialogContoller.ok();
} catch (error) {
console.log(error);
this.errorMessage = (error as Error).message;
}
}

cancel() {
this._dialogContoller.cancel();
}
}