diff --git a/.gitignore b/.gitignore index 642f031..a23889d 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ # Signing application keys *.jks +*.pepk # misc .DS_Store diff --git a/android/app/build.gradle b/android/app/build.gradle index 629fb2f..e74fed1 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -2,12 +2,12 @@ apply plugin: 'com.android.application' android { signingConfigs { - debug { - storeFile file('D:\\Projects\\afu-4-code\\my-keystore.jks') - storePassword System.getenv("KEYSTORE_PASS") - keyAlias 'myalias' - keyPassword System.getenv("KEYSTORE_PASS") - } +// debug { +// storeFile file('D:\\Projects\\afu-4-code\\my-keystore.jks') +// storePassword System.getenv("KEYSTORE_PASS") +// keyAlias 'myalias' +// keyPassword System.getenv("KEYSTORE_PASS") +// } release { storeFile file('D:\\Projects\\afu-4-code\\my-keystore.jks') storePassword System.getenv("KEYSTORE_PASS") @@ -20,8 +20,8 @@ android { applicationId "com.vruzhentsov.afu4code" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 3 - versionName "0.0.3" + versionCode 4 + versionName "0.0.4" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. @@ -35,9 +35,9 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' signingConfig signingConfigs.release } - debug { - signingConfig signingConfigs.release - } +// debug { +// signingConfig signingConfigs.debug +// } } } @@ -59,7 +59,6 @@ dependencies { implementation project(':capacitor-cordova-android-plugins') } - apply from: 'capacitor.build.gradle' try { diff --git a/android/app/capacitor.build.gradle b/android/app/capacitor.build.gradle index 06a5be4..c671eb5 100644 --- a/android/app/capacitor.build.gradle +++ b/android/app/capacitor.build.gradle @@ -10,12 +10,14 @@ android { apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" dependencies { implementation project(':capacitor-app') + implementation project(':capacitor-filesystem') implementation project(':capacitor-haptics') implementation project(':capacitor-keyboard') + implementation project(':capacitor-share') implementation project(':capacitor-status-bar') } - +apply from: "../../node_modules/cordova-plugin-ionic/src/android/cordovapluginionic.gradle" if (hasProperty('postBuildExtras')) { postBuildExtras() diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 6237d3f..ba61803 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -39,4 +39,6 @@ + + diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 85e5a60..c6a12b4 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,7 +1,13 @@ - AFU-4-code - AFU-4-code - com.vruzhentsov.afu4code - com.vruzhentsov.afu4code + AFU-4-code + AFU-4-code + com.vruzhentsov.afu4code + com.vruzhentsov.afu4code + c9d36f67 + Production + background + 2 + 30 + https://api.ionicjs.com diff --git a/android/capacitor.settings.gradle b/android/capacitor.settings.gradle index bd35873..ed072ad 100644 --- a/android/capacitor.settings.gradle +++ b/android/capacitor.settings.gradle @@ -5,11 +5,17 @@ project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/ include ':capacitor-app' project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android') +include ':capacitor-filesystem' +project(':capacitor-filesystem').projectDir = new File('../node_modules/@capacitor/filesystem/android') + include ':capacitor-haptics' project(':capacitor-haptics').projectDir = new File('../node_modules/@capacitor/haptics/android') include ':capacitor-keyboard' project(':capacitor-keyboard').projectDir = new File('../node_modules/@capacitor/keyboard/android') +include ':capacitor-share' +project(':capacitor-share').projectDir = new File('../node_modules/@capacitor/share/android') + include ':capacitor-status-bar' project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android') diff --git a/android/variables.gradle b/android/variables.gradle index 777bd7e..f29cd2e 100644 --- a/android/variables.gradle +++ b/android/variables.gradle @@ -12,5 +12,6 @@ ext { junitVersion = '4.13.2' androidxJunitVersion = '1.1.3' androidxEspressoCoreVersion = '3.4.0' + androidxBrowserVersion = '1.4.0' cordovaAndroidVersion = '10.1.1' } \ No newline at end of file diff --git a/ionic.config.json b/ionic.config.json index 1b59509..bb53d30 100644 --- a/ionic.config.json +++ b/ionic.config.json @@ -3,5 +3,6 @@ "integrations": { "capacitor": {} }, - "type": "react" + "type": "react", + "id": "c9d36f67" } diff --git a/package-lock.json b/package-lock.json index c69c124..12ab641 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,13 @@ { "name": "afu-4-code", - "version": "0.0.1", + "version": "0.0.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "afu-4-code", - "version": "0.0.1", + "version": "0.0.3", + "hasInstallScript": true, "dependencies": { "@capacitor/android": "4.6.1", "@capacitor/app": "4.1.1", @@ -16,6 +17,7 @@ "@capacitor/status-bar": "4.1.1", "@ionic/react": "^6.0.0", "@ionic/react-router": "^6.0.0", + "@ionic/storage": "^3.0.6", "@testing-library/jest-dom": "^5.11.9", "@testing-library/react": "^13.3.0", "@testing-library/user-event": "^12.6.3", @@ -25,6 +27,7 @@ "@types/react-dom": "^18.0.6", "@types/react-router": "^5.1.11", "@types/react-router-dom": "^5.1.7", + "cordova-plugin-ionic": "5.5.3", "history": "^4.9.0", "ionicons": "^6.0.3", "react": "^18.2.0", @@ -33,6 +36,7 @@ "react-router-dom": "^5.2.0", "react-scripts": "^5.0.0", "typescript": "^4.1.3", + "uuid": "^9.0.0", "web-vitals": "^0.2.4", "workbox-background-sync": "^5.1.4", "workbox-broadcast-update": "^5.1.4", @@ -48,7 +52,12 @@ "workbox-streams": "^5.1.4" }, "devDependencies": { - "@capacitor/cli": "4.6.1" + "@capacitor/cli": "4.6.1", + "@types/uuid": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0", + "yarn": ">=3.0" } }, "node_modules/@adobe/css-tools": { @@ -2466,6 +2475,14 @@ "react-router-dom": "^5.0.1" } }, + "node_modules/@ionic/storage": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@ionic/storage/-/storage-3.0.6.tgz", + "integrity": "sha512-sw+zSJINIpbQCGZR9mEtb9N0WmZLuhcMVqOZJBqLuDACAMdXqG39zmp5nSVqhGI1/9X3nd0K5gVn6icyVfUnUg==", + "dependencies": { + "localforage": "^1.9.0" + } + }, "node_modules/@ionic/utils-array": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@ionic/utils-array/-/utils-array-2.1.5.tgz", @@ -3922,6 +3939,12 @@ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" }, + "node_modules/@types/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q==", + "dev": true + }, "node_modules/@types/ws": { "version": "8.5.4", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", @@ -5688,6 +5711,26 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/cordova-plugin-ionic": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/cordova-plugin-ionic/-/cordova-plugin-ionic-5.5.3.tgz", + "integrity": "sha512-b7gycwVk9/TX5NabZCjUg27J2pik/zF2jd3rEhTQUyn3KOx10tT9M0PXoORlnOw4wviQVQ2MvgxM9zlHK+96ow==", + "dependencies": { + "typescript": "3.8.3" + } + }, + "node_modules/cordova-plugin-ionic/node_modules/typescript": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/core-js": { "version": "3.27.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.27.1.tgz", @@ -8686,6 +8729,11 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, "node_modules/immer": { "version": "9.0.18", "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.18.tgz", @@ -10466,6 +10514,14 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lilconfig": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", @@ -10500,6 +10556,14 @@ "node": ">=8.9.0" } }, + "node_modules/localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "dependencies": { + "lie": "3.1.1" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -14009,6 +14073,14 @@ "websocket-driver": "^0.7.4" } }, + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -15158,9 +15230,9 @@ } }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", "bin": { "uuid": "dist/bin/uuid" } @@ -17902,6 +17974,14 @@ "tslib": "*" } }, + "@ionic/storage": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@ionic/storage/-/storage-3.0.6.tgz", + "integrity": "sha512-sw+zSJINIpbQCGZR9mEtb9N0WmZLuhcMVqOZJBqLuDACAMdXqG39zmp5nSVqhGI1/9X3nd0K5gVn6icyVfUnUg==", + "requires": { + "localforage": "^1.9.0" + } + }, "@ionic/utils-array": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@ionic/utils-array/-/utils-array-2.1.5.tgz", @@ -19039,6 +19119,12 @@ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" }, + "@types/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q==", + "dev": true + }, "@types/ws": { "version": "8.5.4", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", @@ -20335,6 +20421,21 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "cordova-plugin-ionic": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/cordova-plugin-ionic/-/cordova-plugin-ionic-5.5.3.tgz", + "integrity": "sha512-b7gycwVk9/TX5NabZCjUg27J2pik/zF2jd3rEhTQUyn3KOx10tT9M0PXoORlnOw4wviQVQ2MvgxM9zlHK+96ow==", + "requires": { + "typescript": "3.8.3" + }, + "dependencies": { + "typescript": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==" + } + } + }, "core-js": { "version": "3.27.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.27.1.tgz", @@ -22525,6 +22626,11 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==" }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, "immer": { "version": "9.0.18", "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.18.tgz", @@ -23835,6 +23941,14 @@ "type-check": "~0.4.0" } }, + "lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "requires": { + "immediate": "~3.0.5" + } + }, "lilconfig": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", @@ -23860,6 +23974,14 @@ "json5": "^2.1.2" } }, + "localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "requires": { + "lie": "3.1.1" + } + }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -26249,6 +26371,13 @@ "faye-websocket": "^0.11.3", "uuid": "^8.3.2", "websocket-driver": "^0.7.4" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } } }, "source-list-map": { @@ -27105,9 +27234,9 @@ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" }, "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" }, "v8-to-istanbul": { "version": "8.1.1", diff --git a/package.json b/package.json index 1f6d6ed..8e9a84b 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,15 @@ { "name": "afu-4-code", - "version": "0.0.3", + "version": "0.0.4", "private": true, "dependencies": { "@capacitor/android": "4.6.1", "@capacitor/app": "4.1.1", "@capacitor/core": "4.6.1", + "@capacitor/filesystem": "^4.1.4", "@capacitor/haptics": "4.1.0", "@capacitor/keyboard": "4.1.0", + "@capacitor/share": "^4.1.1", "@capacitor/status-bar": "4.1.1", "@ionic/react": "^6.0.0", "@ionic/react-router": "^6.0.0", @@ -21,6 +23,7 @@ "@types/react-dom": "^18.0.6", "@types/react-router": "^5.1.11", "@types/react-router-dom": "^5.1.7", + "cordova-plugin-ionic": "5.5.3", "history": "^4.9.0", "ionicons": "^6.0.3", "react": "^18.2.0", diff --git a/screenshoots/main-page-1024x500.jpg b/screenshoots/main-page-1024x500.jpg new file mode 100644 index 0000000..5ee0bc6 Binary files /dev/null and b/screenshoots/main-page-1024x500.jpg differ diff --git a/screenshoots/main-page-500x1024.jpg b/screenshoots/main-page-500x1024.jpg new file mode 100644 index 0000000..0c3004f Binary files /dev/null and b/screenshoots/main-page-500x1024.jpg differ diff --git a/src/interfaces/IDownloadableData.ts b/src/interfaces/IDownloadableData.ts index ad2f35e..76b4766 100644 --- a/src/interfaces/IDownloadableData.ts +++ b/src/interfaces/IDownloadableData.ts @@ -9,3 +9,8 @@ export interface IDownloadableData { export interface IDownloadableCodes extends IDownloadableData{ codes: ICode[] } + +export interface IDownloadableButton { + href: string; + download: string; +} diff --git a/src/pages/Backup.tsx b/src/pages/Backup.tsx index fb3948e..d393ca3 100644 --- a/src/pages/Backup.tsx +++ b/src/pages/Backup.tsx @@ -13,29 +13,29 @@ import { IonIcon, useIonToast } from "@ionic/react"; -import {FC, useContext} from "react"; +import {FC, useCallback, 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"; +import {cloudDownloadOutline, cloudUploadOutline, shareSocialOutline} from "ionicons/icons"; const BackupPage: FC = () => { const {codesRepository, backupService} = useContext(DependencyContext); const [present] = useIonToast(); - const prepareCodesToData = async (): Promise => { + const prepareCodesToData = useCallback(async (): Promise => { return { version: projectPackage.version, codes: await codesRepository.getCodes() } - } + }, []); - const getCodesDataFromSystem = (): Promise => { + const getCodesDataFromSystem = useCallback((): Promise => { return backupService.import(); - }; + }, []); - const onImportReplaceClick = async () => { + const onImportReplaceClick = useCallback(async () => { const codes: ICode[] = await getCodesDataFromSystem(); await codesRepository.setCodes(codes); present({ @@ -45,9 +45,9 @@ const BackupPage: FC = () => { color: "success" }); console.debug("[BackupPage] onImportReplaceClick", {codes}); - } + }, []); - const onImportAddClick = async () => { + const onImportAddClick = useCallback(async () => { const codes: ICode[] = await getCodesDataFromSystem(); await codesRepository.mergeCodes(codes); present({ @@ -57,13 +57,19 @@ const BackupPage: FC = () => { color: "success" }); console.debug("[BackupPage] onImportAddClick", {codes}); - } + }, []); - const onExportClick = async () => { + const onShareClick = useCallback(async () => { + const codesData = await prepareCodesToData() + await backupService.share(codesData) + console.debug("[BackupPage] onShareClick") + }, []); + + const onExportClick = useCallback( async () => { const codesData = await prepareCodesToData() await backupService.export(codesData) console.debug("[BackupPage] onExportClick") - } + }, []); return ( @@ -102,10 +108,22 @@ const BackupPage: FC = () => { - +   Експорт + + + diff --git a/src/services/backup.service.ts b/src/services/backup.service.ts index e2bd574..6f850ba 100644 --- a/src/services/backup.service.ts +++ b/src/services/backup.service.ts @@ -5,16 +5,22 @@ import {IDownloadableCodes} from "../interfaces/IDownloadableData"; export default class BackupService { constructor(private filesStorageService: FilesStorageService) {} async import(): Promise { - const file: string = await this.filesStorageService.open(); + const file: string = await this.filesStorageService.open("text/json"); const {codes} = JSON.parse(file) return codes; } async export(codesData: IDownloadableCodes) { + return this.filesStorageService.downloadFile(BackupService.generateFileName(), codesData); + } + + share(codesData: IDownloadableCodes) { + return this.filesStorageService.shareFile(BackupService.generateFileName(), codesData); + } + + static generateFileName(): string { 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); + return `afu-4-code-backup-${formattedDate}.json`; } } diff --git a/src/services/files-storage.service.ts b/src/services/files-storage.service.ts index c10f60c..22487b1 100644 --- a/src/services/files-storage.service.ts +++ b/src/services/files-storage.service.ts @@ -1,13 +1,64 @@ +import {Directory, Encoding, Filesystem} from '@capacitor/filesystem'; +import {Share} from '@capacitor/share'; +import {Capacitor} from "@capacitor/core"; + export type DownloadableData = Object | Array; 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 shareFile(fileName: string, data: DownloadableData) { + await Filesystem.writeFile({ + path: fileName, + data: JSON.stringify(data), + directory: Directory.Cache, + encoding: Encoding.UTF8, + }); + + const {uri} = await Filesystem.getUri({ + directory: Directory.Cache, + path: fileName, + }); + + await Share.share({ + // title: fileName, + // text: fileName, + url: uri, + // files: [uri], + }); + } + + async downloadFile(fileName: string, data: DownloadableData) { + if (Capacitor.isNativePlatform()) { + const permissionStatus = await Filesystem.checkPermissions(); + + if (permissionStatus.publicStorage !== 'granted') { + await Filesystem.requestPermissions(); + } + + await Filesystem.writeFile({ + path: fileName, + data: JSON.stringify(data), + directory: Directory.Documents, + encoding: Encoding.UTF8 + }); + + } else { + // We're running in a browser, so save the file to the app's data directory + const blob = new Blob([JSON.stringify(data)], {type: "text/json"}); + const link = document.createElement("a"); + + link.download = fileName; + link.href = window.URL.createObjectURL(blob); + link.dataset.downloadurl = ["text/json", link.download, link.href].join(":"); + + const evt = new MouseEvent("click", { + view: window, + bubbles: true, + cancelable: true, + }); + + link.dispatchEvent(evt); + link.remove() + } } async open(ext: string | undefined = undefined): Promise { diff --git a/yarn.lock b/yarn.lock index f22f034..0b09cac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1562,6 +1562,15 @@ __metadata: languageName: node linkType: hard +"@capacitor/filesystem@npm:^4.1.4": + version: 4.1.4 + resolution: "@capacitor/filesystem@npm:4.1.4" + peerDependencies: + "@capacitor/core": ^4.0.0 + checksum: 6c1c1b934e6dfb7866f3a7995fd17bfa436b763ad720d249c72f2dc9febe36e7e0f9fb4f2dc1efcba7abd93323227ff002cc0713b1dcab3bcd24c3fe4f30e0f0 + languageName: node + linkType: hard + "@capacitor/haptics@npm:4.1.0": version: 4.1.0 resolution: "@capacitor/haptics@npm:4.1.0" @@ -1580,6 +1589,15 @@ __metadata: languageName: node linkType: hard +"@capacitor/share@npm:^4.1.1": + version: 4.1.1 + resolution: "@capacitor/share@npm:4.1.1" + peerDependencies: + "@capacitor/core": ^4.0.0 + checksum: 3356d1719040d92331f4c69e7842fd86820b0bffbbca3d7233b1c69d5518304f2d895944e1b0386ad5995b2592eb8f2c699c7740ab8b1fa30489cae093bc840b + languageName: node + linkType: hard + "@capacitor/status-bar@npm:4.1.1": version: 4.1.1 resolution: "@capacitor/status-bar@npm:4.1.1" @@ -3662,8 +3680,10 @@ __metadata: "@capacitor/app": 4.1.1 "@capacitor/cli": 4.6.1 "@capacitor/core": 4.6.1 + "@capacitor/filesystem": ^4.1.4 "@capacitor/haptics": 4.1.0 "@capacitor/keyboard": 4.1.0 + "@capacitor/share": ^4.1.1 "@capacitor/status-bar": 4.1.1 "@ionic/react": ^6.0.0 "@ionic/react-router": ^6.0.0 @@ -3678,6 +3698,7 @@ __metadata: "@types/react-router": ^5.1.11 "@types/react-router-dom": ^5.1.7 "@types/uuid": ^9.0.0 + cordova-plugin-ionic: 5.5.3 history: ^4.9.0 ionicons: ^6.0.3 react: ^18.2.0 @@ -4908,6 +4929,15 @@ __metadata: languageName: node linkType: hard +"cordova-plugin-ionic@npm:5.5.3": + version: 5.5.3 + resolution: "cordova-plugin-ionic@npm:5.5.3" + dependencies: + typescript: 3.8.3 + checksum: c0e4c0b42af6b810058860ca5df55610176e2e785545296852a8f3a18e816acea86884bfdbb1a8966f5a7ece0fb2cced55051e8271e885b0c09dbee97cb685a8 + languageName: node + linkType: hard + "core-js-compat@npm:^3.25.1": version: 3.27.1 resolution: "core-js-compat@npm:3.27.1" @@ -12817,6 +12847,16 @@ __metadata: languageName: node linkType: hard +"typescript@npm:3.8.3": + version: 3.8.3 + resolution: "typescript@npm:3.8.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 3ef07f997a9a3af80d663cc611be984d3e22df3dc2b5ecfa6cbe7dbd54f1984fa1c679ba67e6a9d36d73ffdbbab88a0fbb58eb30ffc4f748f3a3c28d562b546a + languageName: node + linkType: hard + "typescript@npm:^4.1.3": version: 4.9.4 resolution: "typescript@npm:4.9.4" @@ -12827,6 +12867,16 @@ __metadata: languageName: node linkType: hard +"typescript@patch:typescript@3.8.3#~builtin": + version: 3.8.3 + resolution: "typescript@patch:typescript@npm%3A3.8.3#~builtin::version=3.8.3&hash=c44097" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: fba33b9756bbffcbdc87254af0f491852bac475c1302b570b9c656c10fad9dbe7113975a64beb6035979db555b0fc87fa168734f10bf556f173213c13d371e4e + languageName: node + linkType: hard + "typescript@patch:typescript@^4.1.3#~builtin": version: 4.9.4 resolution: "typescript@patch:typescript@npm%3A4.9.4#~builtin::version=4.9.4&hash=ad5954"