Skip to content

Commit

Permalink
#0000 add backup feature. Fix some styles
Browse files Browse the repository at this point in the history
  • Loading branch information
VRuzhentsov committed Feb 21, 2023
1 parent 8117938 commit dca6ad0
Show file tree
Hide file tree
Showing 11 changed files with 218 additions and 10 deletions.
12 changes: 6 additions & 6 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@ android {
signingConfigs {
debug {
storeFile file('D:\\Projects\\afu-4-code\\my-keystore.jks')
storePassword keystorepass
storePassword System.getenv("KEYSTORE_PASS")
keyAlias 'myalias'
keyPassword keystorepass
keyPassword System.getenv("KEYSTORE_PASS")
}
release {
storeFile file('D:\\Projects\\afu-4-code\\my-keystore.jks')
storePassword keystorepass
storePassword System.getenv("KEYSTORE_PASS")
keyAlias 'myalias'
keyPassword keystorepass
keyPassword System.getenv("KEYSTORE_PASS")
}
}
compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig {
applicationId "com.vruzhentsov.afu4code"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
versionCode 3
versionName "0.0.3"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "afu-4-code",
"version": "0.0.2",
"version": "0.0.3",
"private": true,
"dependencies": {
"@capacitor/android": "4.6.1",
Expand Down Expand Up @@ -47,6 +47,8 @@
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"cap:copy:android": "npx cap copy android",
"cap:build:android": "ionic capacitor build android",
"test": "react-scripts test --transformIgnorePatterns 'node_modules/(?!(@ionic/react|@ionic/react-router|@ionic/core|@stencil/core|ionicons)/)'",
"postinstall": "node postinstall.js",
"eject": "react-scripts eject"
Expand Down
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Menu from './components/Menu';
import Page from './pages/Page';
import MainPage from "./pages/MainPage";
import CodesPage from "./pages/CodesPage";
import BackupPage from "./pages/Backup";

/* Core CSS required for Ionic components to work properly */
import '@ionic/react/css/core.css';
Expand Down Expand Up @@ -39,6 +40,7 @@ const App: React.FC = () => {
</Route>
<Route path="/Main" exact={true} component={MainPage}/>
<Route path="/Codes" exact={true} component={CodesPage}/>
<Route path="/Backup" exact={true} component={BackupPage}/>
<Route path="/page/:name" exact={true}>
<Page />
</Route>
Expand Down
7 changes: 7 additions & 0 deletions src/components/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {

import { useLocation } from 'react-router-dom';
import {
downloadOutline,
list,
mailOutline,
mailSharp,
Expand All @@ -37,6 +38,12 @@ const appPages: AppPage[] = [
url: '/Codes',
iosIcon: paperPlaneOutline,
mdIcon: list
},
{
title: 'Бекап',
url: '/Backup',
iosIcon: paperPlaneOutline,
mdIcon: downloadOutline
}
];

Expand Down
10 changes: 9 additions & 1 deletion src/contexts/dependencyContext.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { createContext } from 'react';
import CodesRepository from "../repositories/codes.repository";
import StorageService from "../services/storage.service";
import FilesStorageService from "../services/files-storage.service";
import BackupService from "../services/backup.service";

const storageService = new StorageService()
const filesStorageService = new FilesStorageService()

const DependencyContext = createContext({
codesRepository: new CodesRepository(new StorageService()),
storageService,
codesRepository: new CodesRepository(storageService),
filesStorageService,
backupService: new BackupService(filesStorageService),
});

export default DependencyContext;
11 changes: 11 additions & 0 deletions src/interfaces/IDownloadableData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {ICode} from "./ICode";

export interface IDownloadableData {
version: string;

[x: string | number]: unknown;
}

export interface IDownloadableCodes extends IDownloadableData{
codes: ICode[]
}
117 changes: 117 additions & 0 deletions src/pages/Backup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import {
IonButtons,
IonHeader,
IonMenuButton,
IonTitle,
IonToolbar,
IonPage,
IonContent,
IonButton,
IonGrid,
IonRow,
IonCol,
IonIcon,
useIonToast
} from "@ionic/react";
import {FC, useContext} from "react";
import DependencyContext from "../contexts/dependencyContext";
import {IDownloadableCodes} from "../interfaces/IDownloadableData";
import projectPackage from "../../package.json"
import {ICode} from "../interfaces/ICode.js";
import {cloudDownloadOutline, cloudUploadOutline} from "ionicons/icons";

const BackupPage: FC = () => {
const {codesRepository, backupService} = useContext(DependencyContext);
const [present] = useIonToast();

const prepareCodesToData = async (): Promise<IDownloadableCodes> => {
return {
version: projectPackage.version,
codes: await codesRepository.getCodes()
}
}

const getCodesDataFromSystem = (): Promise<ICode[]> => {
return backupService.import();
};

const onImportReplaceClick = async () => {
const codes: ICode[] = await getCodesDataFromSystem();
await codesRepository.setCodes(codes);
present({
message: 'Успішно імпортовано',
duration: 1500,
position: "bottom",
color: "success"
});
console.debug("[BackupPage] onImportReplaceClick", {codes});
}

const onImportAddClick = async () => {
const codes: ICode[] = await getCodesDataFromSystem();
await codesRepository.mergeCodes(codes);
present({
message: 'Успішно імпортовано',
duration: 1500,
position: "bottom",
color: "success"
});
console.debug("[BackupPage] onImportAddClick", {codes});
}

const onExportClick = async () => {
const codesData = await prepareCodesToData()
await backupService.export(codesData)
console.debug("[BackupPage] onExportClick")
}

return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonMenuButton/>
</IonButtons>
<IonTitle>Бекап</IonTitle>
</IonToolbar>
</IonHeader>

<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Бекап</IonTitle>
</IonToolbar>
</IonHeader>

<IonGrid>
<IonRow className="ion-justify-content-center">
<IonCol size="auto">
<IonButton size="large" onClick={onImportAddClick}>
<IonIcon icon={cloudDownloadOutline} />&nbsp;
Імпорт
</IonButton>
</IonCol>
</IonRow>
<IonRow className="ion-justify-content-center">
<IonCol size="auto">
<IonButton size="large" onClick={onImportReplaceClick}>
<IonIcon icon={cloudDownloadOutline} />&nbsp;
Імпорт (замінити)
</IonButton>
</IonCol>
</IonRow>
<IonRow className="ion-justify-content-center">
<IonCol size="auto">
<IonButton size="large" onClick={onExportClick}>
<IonIcon icon={cloudUploadOutline} />&nbsp;
Експорт
</IonButton>
</IonCol>
</IonRow>
</IonGrid>
</IonContent>
</IonPage>
)
}

export default BackupPage;
4 changes: 2 additions & 2 deletions src/pages/MainPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ const MainPage: React.FC = () => {
<IonCol size="3">
<IonLabel>{item.code}</IonLabel>
</IonCol>
<IonCol size="5">
<IonLabel>{item.description}</IonLabel>
<IonCol size="9">
<IonLabel text-wrap>{item.description}</IonLabel>
</IonCol>
</IonRow>
</IonGrid>
Expand Down
10 changes: 10 additions & 0 deletions src/repositories/codes.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ export default class CodesRepository {
return codes || [];
}

async setCodes(codes: ICode[]) {
await this.storageService.set(this.codesKey, codes);
}

async mergeCodes(codes: ICode[]) {
const oldCodes = await this.storageService.get(this.codesKey);
const newCodes = [...oldCodes, ...codes];
await this.storageService.set(this.codesKey, newCodes);
}

async createCode(code: ICode): Promise<void> {
const codes = await this.getCodes();
codes.push(code);
Expand Down
20 changes: 20 additions & 0 deletions src/services/backup.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import FilesStorageService from "./files-storage.service";
import {ICode} from "../interfaces/ICode.js";
import {IDownloadableCodes} from "../interfaces/IDownloadableData";

export default class BackupService {
constructor(private filesStorageService: FilesStorageService) {}
async import(): Promise<ICode[]> {
const file: string = await this.filesStorageService.open();
const {codes} = JSON.parse(file)
return codes;
}

async export(codesData: IDownloadableCodes) {
const date: Date = new Date();
// const formattedDate = date.getTime();
const formattedDate: string = "" + date.getFullYear() + date.getMonth() + date.getDate() + date.getHours() + date.getMinutes();
const fileName: string = `afu-4-code-backup-${formattedDate}.json`
this.filesStorageService.downloadDataJson(fileName, codesData);
}
}
31 changes: 31 additions & 0 deletions src/services/files-storage.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export type DownloadableData = Object | Array<string>;

export default class FilesStorageService {
downloadDataJson(fileName: string, data: DownloadableData) {
const element = document.createElement("a");
const file: Blob = new Blob([JSON.stringify(data)], {type: 'text/json'});
element.href = URL.createObjectURL(file);
element.download = fileName;
document.body.appendChild(element); // Required for this to work in FireFox
element.click();
}

async open(ext: string | undefined = undefined): Promise<string> {
// open file .json, return file/buffer
return new Promise((resolve, reject) => {
const input = document.createElement('input');
input.type = 'file';
input.accept = ext || 'application/json';
input.onchange = (event: any) => {
const file = event.target.files[0];
const reader = new FileReader();
reader.onload = (event: any) => {
console.debug("[FilesStorageService] open", {event, file});
resolve(event.target.result);
};
reader.readAsText(file);
};
input.click();
})
}
}

0 comments on commit dca6ad0

Please sign in to comment.