├── .eslintcache ├── .gitignore ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── android ├── .gitignore ├── .idea │ ├── .gitignore │ ├── compiler.xml │ ├── deploymentTargetDropDown.xml │ ├── jarRepositories.xml │ └── misc.xml ├── app │ ├── .gitignore │ ├── build.gradle │ ├── capacitor.build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── getcapacitor │ │ │ └── myapp │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ │ ├── capacitor.config.json │ │ │ └── capacitor.plugins.json │ │ ├── java │ │ │ └── com │ │ │ │ └── jeep │ │ │ │ └── app │ │ │ │ └── ionic │ │ │ │ └── react │ │ │ │ └── MainActivity.java │ │ └── res │ │ │ ├── drawable-land-hdpi │ │ │ └── splash.png │ │ │ ├── drawable-land-mdpi │ │ │ └── splash.png │ │ │ ├── drawable-land-xhdpi │ │ │ └── splash.png │ │ │ ├── drawable-land-xxhdpi │ │ │ └── splash.png │ │ │ ├── drawable-land-xxxhdpi │ │ │ └── splash.png │ │ │ ├── drawable-port-hdpi │ │ │ └── splash.png │ │ │ ├── drawable-port-mdpi │ │ │ └── splash.png │ │ │ ├── drawable-port-xhdpi │ │ │ └── splash.png │ │ │ ├── drawable-port-xxhdpi │ │ │ └── splash.png │ │ │ ├── drawable-port-xxxhdpi │ │ │ └── splash.png │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ ├── ic_launcher_background.xml │ │ │ └── splash.png │ │ │ ├── layout │ │ │ └── activity_main.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_round.png │ │ │ ├── values │ │ │ ├── ic_launcher_background.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ │ └── xml │ │ │ ├── config.xml │ │ │ ├── data_extraction_rules.xml │ │ │ └── file_paths.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── getcapacitor │ │ └── myapp │ │ └── ExampleUnitTest.java ├── build.gradle ├── capacitor.settings.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── variables.gradle ├── capacitor.config.ts ├── electron ├── .gitignore ├── assets │ ├── appIcon.ico │ ├── appIcon.png │ ├── splash.gif │ └── splash.png ├── capacitor.config.ts ├── electron-builder.config.json ├── live-runner.js ├── package-lock.json ├── package.json ├── resources │ └── electron-publisher-custom.js ├── src │ ├── index.ts │ ├── preload.ts │ ├── rt │ │ ├── electron-plugins.js │ │ └── electron-rt.ts │ └── setup.ts └── tsconfig.json ├── ionic.config.json ├── ios ├── .gitignore └── App │ ├── App.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata │ ├── App.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ ├── App │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── AppIcon-20x20@1x.png │ │ │ ├── AppIcon-20x20@2x-1.png │ │ │ ├── AppIcon-20x20@2x.png │ │ │ ├── AppIcon-20x20@3x.png │ │ │ ├── AppIcon-29x29@1x.png │ │ │ ├── AppIcon-29x29@2x-1.png │ │ │ ├── AppIcon-29x29@2x.png │ │ │ ├── AppIcon-29x29@3x.png │ │ │ ├── AppIcon-40x40@1x.png │ │ │ ├── AppIcon-40x40@2x-1.png │ │ │ ├── AppIcon-40x40@2x.png │ │ │ ├── AppIcon-40x40@3x.png │ │ │ ├── AppIcon-512@2x.png │ │ │ ├── AppIcon-60x60@2x.png │ │ │ ├── AppIcon-60x60@3x.png │ │ │ ├── AppIcon-76x76@1x.png │ │ │ ├── AppIcon-76x76@2x.png │ │ │ ├── AppIcon-83.5x83.5@2x.png │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── Splash.imageset │ │ │ ├── Contents.json │ │ │ ├── splash-2732x2732-1.png │ │ │ ├── splash-2732x2732-2.png │ │ │ └── splash-2732x2732.png │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── capacitor.config.json │ └── config.xml │ └── Podfile ├── package-lock.json ├── package.json ├── public ├── assets │ ├── databases │ │ ├── databases.json │ │ ├── dbForCopy.db │ │ └── myDBSQLite.db │ ├── icon │ │ ├── favicon.png │ │ └── icon.png │ ├── shapes.svg │ └── sql-wasm.wasm ├── index.html └── manifest.json ├── src ├── App.test.tsx ├── App.tsx ├── Utils │ ├── base64Images.ts │ ├── deleteDBUtil.ts │ ├── encryptedSetUtils.ts │ ├── importJsonUtils.ts │ ├── noEncryptionUtils.ts │ ├── upgrade-database-version.ts │ └── upgradeVersionUtils.ts ├── components │ ├── CopyFromAssets.css │ ├── CopyFromAssets.tsx │ ├── ExistingConnection.css │ ├── ExistingConnection.tsx │ ├── ExploreContainer.css │ ├── ExploreContainer.tsx │ ├── MessageListItem.css │ ├── MessageListItem.tsx │ ├── MigrateDB.css │ ├── MigrateDB.tsx │ ├── NoEncryption.css │ ├── NoEncryption.tsx │ ├── NonConformedDB.css │ ├── NonConformedDB.tsx │ ├── Test2dbs.css │ ├── Test2dbs.tsx │ ├── TestEncryption.css │ ├── TestEncryption.tsx │ ├── TestIssue184.css │ ├── TestIssue184.tsx │ ├── TestJsonImportExport.css │ ├── TestJsonImportExport.tsx │ ├── TestOutput.css │ ├── TestOutput.tsx │ ├── TestUpgradeVersion.css │ └── TestUpgradeVersion.tsx ├── data │ ├── messages.json │ └── messages.ts ├── index.tsx ├── pages │ ├── Tab1.css │ ├── Tab1.tsx │ ├── Tab2.css │ ├── Tab2.tsx │ ├── Tab3.css │ ├── Tab3.tsx │ ├── ViewMessage.css │ ├── ViewMessage.tsx │ ├── ViewTest.css │ └── ViewTest.tsx ├── react-app-env.d.ts ├── reportWebVitals.ts ├── service-worker.ts ├── serviceWorkerRegistration.ts ├── services │ └── DarkModeService.tsx ├── setupTests.ts └── theme │ └── variables.css └── tsconfig.json /.eslintcache: -------------------------------------------------------------------------------- 1 | [{"/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/index.tsx":"1","/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/serviceWorker.ts":"2","/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/App.tsx":"3","/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/pages/Tab1.tsx":"4","/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/pages/Tab3.tsx":"5","/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/pages/Tab2.tsx":"6","/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/components/Test2dbs.tsx":"7","/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/components/NoEncryption.tsx":"8","/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/components/ExploreContainer.tsx":"9","/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/components/ExistingConnection.tsx":"10","/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/components/TestEncryption.tsx":"11","/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/components/TestUpgradeVersion.tsx":"12","/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/components/CopyFromAssets.tsx":"13","/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/components/TestJsonImportExport.tsx":"14","/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/Utils/noEncryptionUtils.ts":"15","/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/Utils/deleteDBUtil.ts":"16","/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/Utils/encryptedSetUtils.ts":"17","/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/Utils/upgradeVersionUtils.ts":"18","/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/Utils/importJsonUtils.ts":"19","/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/Utils/base64Images.ts":"20"},{"size":430,"mtime":1611823506292,"results":"21","hashOfConfig":"22"},{"size":5213,"mtime":1611823506294,"results":"23","hashOfConfig":"22"},{"size":3167,"mtime":1611823506284,"results":"24","hashOfConfig":"22"},{"size":672,"mtime":1611823506293,"results":"25","hashOfConfig":"22"},{"size":672,"mtime":1611823506293,"results":"26","hashOfConfig":"22"},{"size":2958,"mtime":1611823506293,"results":"27","hashOfConfig":"22"},{"size":6114,"mtime":1611823506290,"results":"28","hashOfConfig":"22"},{"size":6739,"mtime":1611823506289,"results":"29","hashOfConfig":"22"},{"size":434,"mtime":1611823506288,"results":"30","hashOfConfig":"22"},{"size":5781,"mtime":1611823506287,"results":"31","hashOfConfig":"22"},{"size":7560,"mtime":1611823506290,"results":"32","hashOfConfig":"22"},{"size":7489,"mtime":1611823506292,"results":"33","hashOfConfig":"22"},{"size":4914,"mtime":1611823506287,"results":"34","hashOfConfig":"22"},{"size":14680,"mtime":1611824132753,"results":"35","hashOfConfig":"22"},{"size":2611,"mtime":1611823506286,"results":"36","hashOfConfig":"22"},{"size":593,"mtime":1611823506285,"results":"37","hashOfConfig":"22"},{"size":2215,"mtime":1611823506285,"results":"38","hashOfConfig":"22"},{"size":2987,"mtime":1611823506286,"results":"39","hashOfConfig":"22"},{"size":5902,"mtime":1611823506285,"results":"40","hashOfConfig":"22"},{"size":2672,"mtime":1611823506284,"results":"41","hashOfConfig":"22"},{"filePath":"42","messages":"43","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},"11pfkhi",{"filePath":"45","messages":"46","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"47","messages":"48","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"49","messages":"50","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"51","messages":"52","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"53","messages":"54","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"55","messages":"56","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"57","messages":"58","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"59","messages":"60","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"61","messages":"62","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"63","messages":"64","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"65","messages":"66","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"67","messages":"68","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"69","messages":"70","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"71","messages":"72","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"73","messages":"74","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"75","messages":"76","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"77","messages":"78","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"79","messages":"80","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"44"},{"filePath":"81","messages":"82","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/index.tsx",[],["83","84"],"/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/serviceWorker.ts",[],"/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/App.tsx",[],"/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/pages/Tab1.tsx",[],"/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/pages/Tab3.tsx",[],"/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/pages/Tab2.tsx",[],"/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/components/Test2dbs.tsx",[],"/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/components/NoEncryption.tsx",[],"/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/components/ExploreContainer.tsx",[],"/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/components/ExistingConnection.tsx",[],"/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/components/TestEncryption.tsx",[],"/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/components/TestUpgradeVersion.tsx",[],"/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/components/CopyFromAssets.tsx",[],"/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/components/TestJsonImportExport.tsx",[],"/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/Utils/noEncryptionUtils.ts",[],"/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/Utils/deleteDBUtil.ts",[],"/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/Utils/encryptedSetUtils.ts",[],"/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/Utils/upgradeVersionUtils.ts",[],"/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/Utils/importJsonUtils.ts",[],"/Users/queaujeanpierre/Development/react-sqlite-apps/react-sqlite-app-starter/src/Utils/base64Images.ts",[],{"ruleId":"85","replacedBy":"86"},{"ruleId":"87","replacedBy":"88"},"no-native-reassign",["89"],"no-negated-in-lhs",["90"],"no-global-assign","no-unsafe-negation"] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | .vscode 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at jepi.queau@free.fr. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 QUEAU Jean Pierre 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |


2 | 3 |

Ionic/React SQLite App Starter

4 |

react-sqlite-app-starter


5 |

6 | A Ionic/React application demonstrating the use of the @capacitor-community/sqlite@latest plugin and may be use as an application starter. 7 | 8 |

9 | 10 |
11 |

12 | 13 | 14 | 15 | 16 | 17 |

18 | 19 | ## Maintainers 20 | 21 | | Maintainer | GitHub | Social | 22 | | ----------------- | ----------------------------------------- | ------ | 23 | | Quéau Jean Pierre | [jepiqueau](https://github.com/jepiqueau) | | 24 | 25 | ## Browser Support 26 | 27 | The plugin follows the guidelines from the `Capacitor Team`, 28 | 29 | - [Capacitor Browser Support](https://capacitorjs.com/docs/v3/web#browser-support) 30 | 31 | meaning that it will not work in IE11 without additional JavaScript transformations, e.g. with [Babel](https://babeljs.io/). 32 | 33 | 34 | ## Installation 🚧 35 | 36 | To start building your App using this Starter App, clone this repo to a new directory: 37 | 38 | ```bash 39 | git clone https://github.com/jepiqueau/react-sqlite-app-starter.git 40 | cd react-sqlite-app-starter 41 | git remote rm origin 42 | ``` 43 | 44 | - then install it 45 | 46 | ```bash 47 | npm install 48 | ``` 49 | 50 | - then go to the building process 51 | 52 | ```bash 53 | npm run build 54 | npx cap sync 55 | npm run build 56 | npx cap copy 57 | npx cap copy web 58 | ``` 59 | 60 | the capacitor config parameters are: 61 | 62 | ``` 63 | "appId": "com.example.app.capacitor", 64 | "appName": "react-sqlite-app-starter", 65 | ``` 66 | 67 | ### Building Web Code 68 | 69 | The ```@capacitor-community/sqlite``` is not implemented for Web Browsers. 70 | if you run 71 | 72 | ```bash 73 | npx cap serve 74 | ``` 75 | you will get the following messages: 76 | ``` 77 | SQLite Plugin not available for Web Platform 78 | ``` 79 | 80 | ### Building Native Project 81 | 82 | #### IOS 83 | 84 | ```bash 85 | npx cap open ios 86 | ``` 87 | Once Xcode launches, you can build your finally app binary through the standard Xcode workflow. 88 | 89 | #### Android 90 | 91 | ```bash 92 | npx cap open android 93 | ``` 94 | Once Android Studio launches, you can build your app through the standard Android Studio workflow. 95 | 96 | ## Usage 97 | 98 | 99 | The `@capacitor-community/sqlite` test is accessible in the Tab2 of the Application by clicking on several SQLite test button : 100 | 101 | - SQLite No Encryption Test 102 | - SQLite Two DBs Tests 103 | - SQLite Encryption Test (iOS && Android only) 104 | - SQLite Upgrade Version Test 105 | - SQLite Json Import Export Test 106 | 107 | After having run the `SQLite Two DBs Tests` another test becomes accessible `SQLite Existing Test` which is using the existing connections created in `SQLite Two DBs Tests`. 108 | 109 | The application uses the React Hook `react-sqlite-hook refactor` to access the `@capacitor-community/sqlite refactor` API. 110 | 111 | - [react-sqlite-hook](https://github.com/jepiqueau/react-sqlite-hook/blob/master/README.md) 112 | 113 | ## Contributors ✨ 114 | 115 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 |

Jean Pierre Quéau

💻
125 | 126 | 127 | 128 | 129 | 130 | 131 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | # Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore 2 | 3 | # Built application files 4 | *.apk 5 | *.aar 6 | *.ap_ 7 | *.aab 8 | 9 | # Files for the ART/Dalvik VM 10 | *.dex 11 | 12 | # Java class files 13 | *.class 14 | 15 | # Generated files 16 | bin/ 17 | gen/ 18 | out/ 19 | # Uncomment the following line in case you need and you don't have the release build type files in your app 20 | # release/ 21 | 22 | # Gradle files 23 | .gradle/ 24 | build/ 25 | 26 | # Local configuration file (sdk path, etc) 27 | local.properties 28 | 29 | # Proguard folder generated by Eclipse 30 | proguard/ 31 | 32 | # Log Files 33 | *.log 34 | 35 | # Android Studio Navigation editor temp files 36 | .navigation/ 37 | 38 | # Android Studio captures folder 39 | captures/ 40 | 41 | # IntelliJ 42 | *.iml 43 | .idea/workspace.xml 44 | .idea/tasks.xml 45 | .idea/gradle.xml 46 | .idea/assetWizardSettings.xml 47 | .idea/dictionaries 48 | .idea/libraries 49 | # Android Studio 3 in .gitignore file. 50 | .idea/caches 51 | .idea/modules.xml 52 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you 53 | .idea/navEditor.xml 54 | 55 | # Keystore files 56 | # Uncomment the following lines if you do not want to check your keystore files in. 57 | #*.jks 58 | #*.keystore 59 | 60 | # External native build folder generated in Android Studio 2.2 and later 61 | .externalNativeBuild 62 | .cxx/ 63 | 64 | # Google Services (e.g. APIs or Firebase) 65 | # google-services.json 66 | 67 | # Freeline 68 | freeline.py 69 | freeline/ 70 | freeline_project_description.json 71 | 72 | # fastlane 73 | fastlane/report.xml 74 | fastlane/Preview.html 75 | fastlane/screenshots 76 | fastlane/test_output 77 | fastlane/readme.md 78 | 79 | # Version control 80 | vcs.xml 81 | 82 | # lint 83 | lint/intermediates/ 84 | lint/generated/ 85 | lint/outputs/ 86 | lint/tmp/ 87 | # lint/reports/ 88 | 89 | # Android Profiling 90 | *.hprof 91 | 92 | # Cordova plugins for Capacitor 93 | capacitor-cordova-android-plugins 94 | 95 | # Copied web assets 96 | app/src/main/assets/public 97 | 98 | # Generated Config files 99 | app/src/main/assets/capacitor.config.json 100 | app/src/main/assets/capacitor.plugins.json 101 | app/src/main/res/xml/config.xml 102 | -------------------------------------------------------------------------------- /android/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /android/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /android/.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /android/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | -------------------------------------------------------------------------------- /android/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build/* 2 | !/build/.npmkeep 3 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.compileSdkVersion 5 | defaultConfig { 6 | applicationId "com.jeep.app.ionic.react" 7 | minSdkVersion rootProject.ext.minSdkVersion 8 | targetSdkVersion rootProject.ext.targetSdkVersion 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 12 | aaptOptions { 13 | // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. 14 | // Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61 15 | ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~' 16 | } 17 | } 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | } 25 | 26 | repositories { 27 | flatDir{ 28 | dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs' 29 | } 30 | } 31 | 32 | dependencies { 33 | implementation fileTree(include: ['*.jar'], dir: 'libs') 34 | implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" 35 | implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion" 36 | implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion" 37 | implementation project(':capacitor-android') 38 | testImplementation "junit:junit:$junitVersion" 39 | androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" 40 | androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" 41 | implementation project(':capacitor-cordova-android-plugins') 42 | } 43 | 44 | apply from: 'capacitor.build.gradle' 45 | 46 | try { 47 | def servicesJSON = file('google-services.json') 48 | if (servicesJSON.text) { 49 | apply plugin: 'com.google.gms.google-services' 50 | } 51 | } catch(Exception e) { 52 | logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work") 53 | } 54 | -------------------------------------------------------------------------------- /android/app/capacitor.build.gradle: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN 2 | 3 | android { 4 | compileOptions { 5 | sourceCompatibility JavaVersion.VERSION_11 6 | targetCompatibility JavaVersion.VERSION_11 7 | } 8 | } 9 | 10 | apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" 11 | dependencies { 12 | implementation project(':capacitor-community-sqlite') 13 | implementation project(':capacitor-dialog') 14 | implementation project(':capacitor-toast') 15 | 16 | } 17 | 18 | 19 | if (hasProperty('postBuildExtras')) { 20 | postBuildExtras() 21 | } 22 | -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.getcapacitor.myapp; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import android.content.Context; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | import androidx.test.platform.app.InstrumentationRegistry; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * @see Testing documentation 15 | */ 16 | @RunWith(AndroidJUnit4.class) 17 | public class ExampleInstrumentedTest { 18 | 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 23 | 24 | assertEquals("com.getcapacitor.app", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 14 | 15 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 35 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /android/app/src/main/assets/capacitor.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "appId": "com.jeep.app.ionic.react", 3 | "appName": "react-sqlite-app-starter", 4 | "webDir": "build", 5 | "bundledWebRuntime": false, 6 | "plugins": { 7 | "CapacitorSQLite": { 8 | "iosDatabaseLocation": "Library/CapacitorDatabase", 9 | "iosIsEncryption": true, 10 | "iosKeychainPrefix": "react-sqlite-app-starter", 11 | "iosBiometric": { 12 | "biometricAuth": false, 13 | "biometricTitle": "Biometric login for capacitor sqlite" 14 | }, 15 | "androidIsEncryption": true, 16 | "androidBiometric": { 17 | "biometricAuth": false, 18 | "biometricTitle": "Biometric login for capacitor sqlite", 19 | "biometricSubTitle": "Log in using your biometric" 20 | }, 21 | "electronWindowsLocation": "C:\\ProgramData\\CapacitorDatabases", 22 | "electronMacLocation": "/Volumes/Development_Lacie/Development/CapacitorDatabases", 23 | "electronLinuxLocation": "Databases" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /android/app/src/main/assets/capacitor.plugins.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pkg": "@capacitor-community/sqlite", 4 | "classpath": "com.getcapacitor.community.database.sqlite.CapacitorSQLitePlugin" 5 | }, 6 | { 7 | "pkg": "@capacitor/dialog", 8 | "classpath": "com.capacitorjs.plugins.dialog.DialogPlugin" 9 | }, 10 | { 11 | "pkg": "@capacitor/toast", 12 | "classpath": "com.capacitorjs.plugins.toast.ToastPlugin" 13 | } 14 | ] 15 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/jeep/app/ionic/react/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.jeep.app.ionic.react; 2 | 3 | import com.getcapacitor.BridgeActivity; 4 | 5 | public class MainActivity extends BridgeActivity {} 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-land-hdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/drawable-land-hdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-land-mdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/drawable-land-mdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-land-xhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/drawable-land-xhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-land-xxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/drawable-land-xxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-land-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/drawable-land-xxxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-port-hdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/drawable-port-hdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-port-mdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/drawable-port-mdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-port-xhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/drawable-port-xhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-port-xxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/drawable-port-xxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-port-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/drawable-port-xxxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/drawable/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | react-sqlite-app-starter 4 | react-sqlite-app-starter 5 | com.jeep.app.ionic.react 6 | com.jeep.app.ionic.react 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 17 | 18 | 19 | 22 | -------------------------------------------------------------------------------- /android/app/src/main/res/xml/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /android/app/src/main/res/xml/file_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.getcapacitor.myapp; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | 14 | @Test 15 | public void addition_isCorrect() throws Exception { 16 | assertEquals(4, 2 + 2); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | mavenCentral() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:7.2.2' 11 | classpath 'com.google.gms:google-services:4.3.13' 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | apply from: "variables.gradle" 19 | 20 | allprojects { 21 | repositories { 22 | google() 23 | mavenCentral() 24 | } 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /android/capacitor.settings.gradle: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN 2 | include ':capacitor-android' 3 | project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor') 4 | 5 | include ':capacitor-community-sqlite' 6 | project(':capacitor-community-sqlite').projectDir = new File('../node_modules/@capacitor-community/sqlite/android') 7 | 8 | include ':capacitor-dialog' 9 | project(':capacitor-dialog').projectDir = new File('../node_modules/@capacitor/dialog/android') 10 | 11 | include ':capacitor-toast' 12 | project(':capacitor-toast').projectDir = new File('../node_modules/@capacitor/toast/android') 13 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | 19 | # AndroidX package structure to make it clearer which packages are bundled with the 20 | # Android operating system, and which are packaged with your app's APK 21 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 22 | android.useAndroidX=true 23 | # Automatically convert third-party libraries to use AndroidX 24 | android.enableJetifier=true 25 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | include ':capacitor-cordova-android-plugins' 3 | project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/') 4 | 5 | apply from: 'capacitor.settings.gradle' -------------------------------------------------------------------------------- /android/variables.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | minSdkVersion = 22 3 | compileSdkVersion = 33 4 | targetSdkVersion = 33 5 | androidxActivityVersion = '1.4.0' 6 | androidxAppCompatVersion = '1.6.1' 7 | androidxCoordinatorLayoutVersion = '1.2.0' 8 | androidxCoreVersion = '1.8.0' 9 | androidxFragmentVersion = '1.4.1' 10 | coreSplashScreenVersion = '1.0.0-rc01' 11 | androidxWebkitVersion = '1.4.0' 12 | junitVersion = '4.13.2' 13 | androidxJunitVersion = '1.1.5' 14 | androidxEspressoCoreVersion = '3.5.1' 15 | cordovaAndroidVersion = '10.1.1' 16 | } -------------------------------------------------------------------------------- /capacitor.config.ts: -------------------------------------------------------------------------------- 1 | import { CapacitorConfig } from '@capacitor/cli'; 2 | 3 | const config: CapacitorConfig = { 4 | appId: 'com.jeep.app.ionic.react', 5 | appName: 'react-sqlite-app-starter', 6 | webDir: 'build', 7 | bundledWebRuntime: false, 8 | plugins: { 9 | CapacitorSQLite: { 10 | iosDatabaseLocation: 'Library/CapacitorDatabase', 11 | iosIsEncryption: true, 12 | iosKeychainPrefix: 'react-sqlite-app-starter', 13 | iosBiometric: { 14 | biometricAuth: false, 15 | biometricTitle : "Biometric login for capacitor sqlite" 16 | }, 17 | androidIsEncryption: true, 18 | androidBiometric: { 19 | biometricAuth : false, 20 | biometricTitle : "Biometric login for capacitor sqlite", 21 | biometricSubTitle : "Log in using your biometric" 22 | }, 23 | electronWindowsLocation: "C:\\ProgramData\\CapacitorDatabases", 24 | electronMacLocation: "/Volumes/Development_Lacie/Development/CapacitorDatabases", 25 | electronLinuxLocation: "Databases" 26 | } 27 | } 28 | }; 29 | 30 | export default config; 31 | -------------------------------------------------------------------------------- /electron/.gitignore: -------------------------------------------------------------------------------- 1 | # NPM renames .gitignore to .npmignore 2 | # In order to prevent that, we remove the initial "." 3 | # And the CLI then renames it 4 | app 5 | node_modules 6 | build 7 | dist 8 | logs 9 | -------------------------------------------------------------------------------- /electron/assets/appIcon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/electron/assets/appIcon.ico -------------------------------------------------------------------------------- /electron/assets/appIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/electron/assets/appIcon.png -------------------------------------------------------------------------------- /electron/assets/splash.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/electron/assets/splash.gif -------------------------------------------------------------------------------- /electron/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/electron/assets/splash.png -------------------------------------------------------------------------------- /electron/capacitor.config.ts: -------------------------------------------------------------------------------- 1 | import { CapacitorConfig } from '@capacitor/cli'; 2 | 3 | const config: CapacitorConfig = { 4 | appId: 'com.jeep.app.ionic.react', 5 | appName: 'react-sqlite-app-starter', 6 | webDir: 'build', 7 | bundledWebRuntime: false, 8 | plugins: { 9 | CapacitorSQLite: { 10 | iosDatabaseLocation: 'Library/CapacitorDatabase', 11 | iosIsEncryption: true, 12 | iosKeychainPrefix: 'react-sqlite-app-starter', 13 | iosBiometric: { 14 | biometricAuth: false, 15 | biometricTitle : "Biometric login for capacitor sqlite" 16 | }, 17 | androidIsEncryption: true, 18 | androidBiometric: { 19 | biometricAuth : false, 20 | biometricTitle : "Biometric login for capacitor sqlite", 21 | biometricSubTitle : "Log in using your biometric" 22 | }, 23 | electronWindowsLocation: "C:\\ProgramData\\CapacitorDatabases", 24 | electronMacLocation: "/Volumes/Development_Lacie/Development/CapacitorDatabases", 25 | electronLinuxLocation: "Databases" 26 | } 27 | } 28 | }; 29 | 30 | export default config; 31 | -------------------------------------------------------------------------------- /electron/electron-builder.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "appId": "com.yourdoamnin.yourapp", 3 | "directories": { 4 | "buildResources": "resources" 5 | }, 6 | "files": [ 7 | "assets/**/*", 8 | "build/**/*", 9 | "capacitor.config.*", 10 | "app/**/*" 11 | ], 12 | "publish": { 13 | "provider": "github" 14 | }, 15 | "nsis": { 16 | "allowElevation": true, 17 | "oneClick": false, 18 | "allowToChangeInstallationDirectory": true 19 | }, 20 | "win": { 21 | "target": "nsis", 22 | "icon": "assets/appIcon.ico" 23 | }, 24 | "mac": { 25 | "category": "your.app.category.type", 26 | "target": "dmg" 27 | } 28 | } -------------------------------------------------------------------------------- /electron/live-runner.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | /* eslint-disable @typescript-eslint/no-var-requires */ 3 | const cp = require('child_process'); 4 | const chokidar = require('chokidar'); 5 | const electron = require('electron'); 6 | 7 | let child = null; 8 | const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm'; 9 | const reloadWatcher = { 10 | debouncer: null, 11 | ready: false, 12 | watcher: null, 13 | restarting: false, 14 | }; 15 | 16 | ///* 17 | function runBuild() { 18 | return new Promise((resolve, _reject) => { 19 | let tempChild = cp.spawn(npmCmd, ['run', 'build']); 20 | tempChild.once('exit', () => { 21 | resolve(); 22 | }); 23 | tempChild.stdout.pipe(process.stdout); 24 | }); 25 | } 26 | //*/ 27 | 28 | async function spawnElectron() { 29 | if (child !== null) { 30 | child.stdin.pause(); 31 | child.kill(); 32 | child = null; 33 | await runBuild(); 34 | } 35 | child = cp.spawn(electron, ['--inspect=5858', './']); 36 | child.on('exit', () => { 37 | if (!reloadWatcher.restarting) { 38 | process.exit(0); 39 | } 40 | }); 41 | child.stdout.pipe(process.stdout); 42 | } 43 | 44 | function setupReloadWatcher() { 45 | reloadWatcher.watcher = chokidar 46 | .watch('./src/**/*', { 47 | ignored: /[/\\]\./, 48 | persistent: true, 49 | }) 50 | .on('ready', () => { 51 | reloadWatcher.ready = true; 52 | }) 53 | .on('all', (_event, _path) => { 54 | if (reloadWatcher.ready) { 55 | clearTimeout(reloadWatcher.debouncer); 56 | reloadWatcher.debouncer = setTimeout(async () => { 57 | console.log('Restarting'); 58 | reloadWatcher.restarting = true; 59 | await spawnElectron(); 60 | reloadWatcher.restarting = false; 61 | reloadWatcher.ready = false; 62 | clearTimeout(reloadWatcher.debouncer); 63 | reloadWatcher.debouncer = null; 64 | reloadWatcher.watcher = null; 65 | setupReloadWatcher(); 66 | }, 500); 67 | } 68 | }); 69 | } 70 | 71 | (async () => { 72 | await runBuild(); 73 | await spawnElectron(); 74 | setupReloadWatcher(); 75 | })(); 76 | -------------------------------------------------------------------------------- /electron/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-sqlite-app-starter", 3 | "version": "1.0.0", 4 | "description": "An Amazing Capacitor App", 5 | "author": { 6 | "name": "", 7 | "email": "" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/jepiqueau/react-sqlite-app-starter.git" 12 | }, 13 | "license": "MIT", 14 | "main": "build/src/index.js", 15 | "scripts": { 16 | "build": "tsc && electron-rebuild", 17 | "electron:start-live": "node ./live-runner.js", 18 | "electron:start": "npm run build && electron --inspect=5858 ./", 19 | "electron:pack": "npm run build && electron-builder build --dir -c ./electron-builder.config.json", 20 | "electron:make": "npm run build && electron-builder build -c ./electron-builder.config.json -p always" 21 | }, 22 | "dependencies": { 23 | "@capacitor-community/electron": "^4.1.0", 24 | "@capacitor-community/sqlite": "^4.6.3-3", 25 | "chokidar": "~3.5.2", 26 | "electron-is-dev": "~2.0.0", 27 | "electron-serve": "~1.1.0", 28 | "electron-unhandled": "~3.0.2", 29 | "electron-updater": "~4.3.9", 30 | "electron-window-state": "~5.0.3", 31 | "jszip": "^3.10.1", 32 | "node-fetch": "^2.6.7", 33 | "sqlite3": "^5.1.2" 34 | }, 35 | "devDependencies": { 36 | "@types/sqlite3": "^3.1.8", 37 | "ansi-regex": "^6.0.1", 38 | "electron": "^18.3.7", 39 | "electron-builder": "~24.0.0", 40 | "electron-rebuild": "^3.2.3", 41 | "typescript": "~4.3.5" 42 | }, 43 | "keywords": [ 44 | "capacitor", 45 | "electron" 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /electron/resources/electron-publisher-custom.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | /* eslint-disable @typescript-eslint/no-var-requires */ 3 | const electronPublish = require('electron-publish'); 4 | 5 | class Publisher extends electronPublish.Publisher { 6 | async upload(task) { 7 | console.log('electron-publisher-custom', task.file); 8 | } 9 | } 10 | module.exports = Publisher; 11 | -------------------------------------------------------------------------------- /electron/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { CapacitorElectronConfig } from '@capacitor-community/electron'; 2 | import { 3 | getCapacitorElectronConfig, 4 | setupElectronDeepLinking, 5 | } from '@capacitor-community/electron'; 6 | import type { MenuItemConstructorOptions } from 'electron'; 7 | import { app, MenuItem } from 'electron'; 8 | import electronIsDev from 'electron-is-dev'; 9 | import unhandled from 'electron-unhandled'; 10 | import { autoUpdater } from 'electron-updater'; 11 | 12 | import { 13 | ElectronCapacitorApp, 14 | setupContentSecurityPolicy, 15 | setupReloadWatcher, 16 | } from './setup'; 17 | 18 | // Graceful handling of unhandled errors. 19 | unhandled(); 20 | 21 | // Define our menu templates (these are optional) 22 | const trayMenuTemplate: (MenuItemConstructorOptions | MenuItem)[] = [ 23 | new MenuItem({ label: 'Quit App', role: 'quit' }), 24 | ]; 25 | const appMenuBarMenuTemplate: (MenuItemConstructorOptions | MenuItem)[] = [ 26 | { role: process.platform === 'darwin' ? 'appMenu' : 'fileMenu' }, 27 | { role: 'viewMenu' }, 28 | ]; 29 | 30 | // Get Config options from capacitor.config 31 | const capacitorFileConfig: CapacitorElectronConfig = 32 | getCapacitorElectronConfig(); 33 | 34 | // Initialize our app. You can pass menu templates into the app here. 35 | // const myCapacitorApp = new ElectronCapacitorApp(capacitorFileConfig); 36 | const myCapacitorApp = new ElectronCapacitorApp( 37 | capacitorFileConfig, 38 | trayMenuTemplate, 39 | appMenuBarMenuTemplate, 40 | ); 41 | 42 | // If deeplinking is enabled then we will set it up here. 43 | if (capacitorFileConfig.electron?.deepLinkingEnabled) { 44 | setupElectronDeepLinking(myCapacitorApp, { 45 | customProtocol: 46 | capacitorFileConfig.electron.deepLinkingCustomProtocol ?? 47 | 'mycapacitorapp', 48 | }); 49 | } 50 | 51 | // If we are in Dev mode, use the file watcher components. 52 | if (electronIsDev) { 53 | setupReloadWatcher(myCapacitorApp); 54 | } 55 | 56 | // Run Application 57 | (async () => { 58 | // Wait for electron app to be ready. 59 | await app.whenReady(); 60 | // Security - Set Content-Security-Policy based on whether or not we are in dev mode. 61 | setupContentSecurityPolicy(myCapacitorApp.getCustomURLScheme()); 62 | // Initialize our app, build windows, and load content. 63 | await myCapacitorApp.init(); 64 | // Check for updates if we are in a packaged app. 65 | autoUpdater.checkForUpdatesAndNotify(); 66 | })(); 67 | 68 | // Handle when all of our windows are close (platforms have their own expectations). 69 | app.on('window-all-closed', function () { 70 | // On OS X it is common for applications and their menu bar 71 | // to stay active until the user quits explicitly with Cmd + Q 72 | if (process.platform !== 'darwin') { 73 | app.quit(); 74 | } 75 | }); 76 | 77 | // When the dock icon is clicked. 78 | app.on('activate', async function () { 79 | // On OS X it's common to re-create a window in the app when the 80 | // dock icon is clicked and there are no other windows open. 81 | if (myCapacitorApp.getMainWindow().isDestroyed()) { 82 | await myCapacitorApp.init(); 83 | } 84 | }); 85 | 86 | // Place all ipc or other electron api calls and custom functionality under this line 87 | -------------------------------------------------------------------------------- /electron/src/preload.ts: -------------------------------------------------------------------------------- 1 | require('./rt/electron-rt'); 2 | ////////////////////////////// 3 | // User Defined Preload scripts below 4 | console.log('User Preload!'); 5 | -------------------------------------------------------------------------------- /electron/src/rt/electron-plugins.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | const CapacitorCommunitySqlite = require('../../../node_modules/@capacitor-community/sqlite/electron/dist/plugin.js'); 3 | 4 | module.exports = { 5 | CapacitorCommunitySqlite, 6 | } -------------------------------------------------------------------------------- /electron/src/rt/electron-rt.ts: -------------------------------------------------------------------------------- 1 | import { ipcRenderer, contextBridge } from 'electron'; 2 | 3 | //////////////////////////////////////////////////////// 4 | // eslint-disable-next-line @typescript-eslint/no-var-requires 5 | const plugins = require('./electron-plugins'); 6 | 7 | const contextApi: { 8 | [plugin: string]: { [functionName: string]: () => Promise }; 9 | } = {}; 10 | for (const pluginKey of Object.keys(plugins)) { 11 | for (const classKey of Object.keys(plugins[pluginKey]).filter( 12 | className => className !== 'default', 13 | )) { 14 | const functionList = Object.getOwnPropertyNames( 15 | plugins[pluginKey][classKey].prototype, 16 | ).filter(v => v !== 'constructor'); 17 | if (!contextApi[classKey]) { 18 | contextApi[classKey] = {}; 19 | } 20 | for (const functionName of functionList) { 21 | if (!contextApi[classKey][functionName]) { 22 | contextApi[classKey][functionName] = (...args) => 23 | ipcRenderer.invoke(`${classKey}-${functionName}`, ...args); 24 | } 25 | } 26 | } 27 | } 28 | contextBridge.exposeInMainWorld('CapacitorCustomPlatform', { 29 | name: 'electron', 30 | plugins: contextApi, 31 | }); 32 | //////////////////////////////////////////////////////// 33 | -------------------------------------------------------------------------------- /electron/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "include": ["./src/**/*", "./capacitor.config.ts", "./capacitor.config.js"], 4 | "compilerOptions": { 5 | "outDir": "./build", 6 | "importHelpers": true, 7 | "target": "ES2017", 8 | "module": "CommonJS", 9 | "moduleResolution": "node", 10 | "esModuleInterop": true, 11 | "typeRoots": ["./node_modules/@types"], 12 | "allowJs": true, 13 | "rootDir": "." 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ionic.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-sqlite-app-starter", 3 | "integrations": { 4 | "capacitor": {} 5 | }, 6 | "type": "react" 7 | } 8 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | App/build 2 | App/Pods 3 | App/Podfile.lock 4 | App/App/public 5 | DerivedData 6 | xcuserdata 7 | 8 | # Cordova plugins for Capacitor 9 | capacitor-cordova-ios-plugins 10 | 11 | # Generated Config files 12 | App/App/capacitor.config.json 13 | App/App/config.xml 14 | -------------------------------------------------------------------------------- /ios/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/App/App.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/App/App/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Capacitor 3 | 4 | @UIApplicationMain 5 | class AppDelegate: UIResponder, UIApplicationDelegate { 6 | 7 | var window: UIWindow? 8 | 9 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 10 | // Override point for customization after application launch. 11 | return true 12 | } 13 | 14 | func applicationWillResignActive(_ application: UIApplication) { 15 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 16 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 17 | } 18 | 19 | func applicationDidEnterBackground(_ application: UIApplication) { 20 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 21 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 22 | } 23 | 24 | func applicationWillEnterForeground(_ application: UIApplication) { 25 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 26 | } 27 | 28 | func applicationDidBecomeActive(_ application: UIApplication) { 29 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 30 | } 31 | 32 | func applicationWillTerminate(_ application: UIApplication) { 33 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 34 | } 35 | 36 | func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { 37 | // Called when the app was launched with a url. Feel free to add additional processing here, 38 | // but if you want the App API to support tracking app url opens, make sure to keep this call 39 | return ApplicationDelegateProxy.shared.application(app, open: url, options: options) 40 | } 41 | 42 | func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { 43 | // Called when the app was launched with an activity, including Universal Links. 44 | // Feel free to add additional processing here, but if you want the App API to support 45 | // tracking app url opens, make sure to keep this call 46 | return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler) 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "AppIcon-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "AppIcon-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "AppIcon-29x29@2x-1.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "AppIcon-29x29@3x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "AppIcon-40x40@2x.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "AppIcon-40x40@3x.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "AppIcon-60x60@2x.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "AppIcon-60x60@3x.png", 49 | "scale" : "3x" 50 | }, 51 | { 52 | "size" : "20x20", 53 | "idiom" : "ipad", 54 | "filename" : "AppIcon-20x20@1x.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "AppIcon-20x20@2x-1.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "29x29", 65 | "idiom" : "ipad", 66 | "filename" : "AppIcon-29x29@1x.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "AppIcon-29x29@2x.png", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "40x40", 77 | "idiom" : "ipad", 78 | "filename" : "AppIcon-40x40@1x.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "AppIcon-40x40@2x-1.png", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "size" : "76x76", 89 | "idiom" : "ipad", 90 | "filename" : "AppIcon-76x76@1x.png", 91 | "scale" : "1x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "AppIcon-76x76@2x.png", 97 | "scale" : "2x" 98 | }, 99 | { 100 | "size" : "83.5x83.5", 101 | "idiom" : "ipad", 102 | "filename" : "AppIcon-83.5x83.5@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "1024x1024", 107 | "idiom" : "ios-marketing", 108 | "filename" : "AppIcon-512@2x.png", 109 | "scale" : "1x" 110 | } 111 | ], 112 | "info" : { 113 | "version" : 1, 114 | "author" : "xcode" 115 | } 116 | } -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/Splash.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "splash-2732x2732-2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "splash-2732x2732-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "splash-2732x2732.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png -------------------------------------------------------------------------------- /ios/App/App/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /ios/App/App/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /ios/App/App/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | react-sqlite-app-starter 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UIViewControllerBasedStatusBarAppearance 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /ios/App/App/capacitor.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "appId": "com.jeep.app.ionic.react", 3 | "appName": "react-sqlite-app-starter", 4 | "webDir": "build", 5 | "bundledWebRuntime": false, 6 | "plugins": { 7 | "CapacitorSQLite": { 8 | "iosDatabaseLocation": "Library/CapacitorDatabase", 9 | "iosIsEncryption": true, 10 | "iosKeychainPrefix": "react-sqlite-app-starter", 11 | "iosBiometric": { 12 | "biometricAuth": false, 13 | "biometricTitle": "Biometric login for capacitor sqlite" 14 | }, 15 | "androidIsEncryption": true, 16 | "androidBiometric": { 17 | "biometricAuth": false, 18 | "biometricTitle": "Biometric login for capacitor sqlite", 19 | "biometricSubTitle": "Log in using your biometric" 20 | }, 21 | "electronWindowsLocation": "C:\\ProgramData\\CapacitorDatabases", 22 | "electronMacLocation": "/Volumes/Development_Lacie/Development/CapacitorDatabases", 23 | "electronLinuxLocation": "Databases" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ios/App/App/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ios/App/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../../node_modules/@capacitor/ios/scripts/pods_helpers' 2 | 3 | platform :ios, '13.0' 4 | use_frameworks! 5 | 6 | # workaround to avoid Xcode caching of Pods that requires 7 | # Product -> Clean Build Folder after new Cordova plugins installed 8 | # Requires CocoaPods 1.6 or newer 9 | install! 'cocoapods', :disable_input_output_paths => true 10 | 11 | def capacitor_pods 12 | pod 'Capacitor', :path => '../../node_modules/@capacitor/ios' 13 | pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios' 14 | pod 'CapacitorCommunitySqlite', :path => '../../node_modules/@capacitor-community/sqlite' 15 | pod 'CapacitorDialog', :path => '../../node_modules/@capacitor/dialog' 16 | pod 'CapacitorToast', :path => '../../node_modules/@capacitor/toast' 17 | end 18 | 19 | target 'App' do 20 | capacitor_pods 21 | # Add your Pods here 22 | end 23 | 24 | post_install do |installer| 25 | assertDeploymentTarget(installer) 26 | end 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-sqlite-app-starter", 3 | "version": "4.2.1", 4 | "private": true, 5 | "description": "Ionic/React SQLite Application Starter", 6 | "homepage": "./", 7 | "author": "Jean Pierre Quéau", 8 | "license": "MIT", 9 | "dependencies": { 10 | "@capacitor-community/electron": "^4.1.2", 11 | "@capacitor-community/sqlite": "^4.6.3-3", 12 | "@capacitor/android": "^4.6.3", 13 | "@capacitor/core": "^4.6.3", 14 | "@capacitor/dialog": "^4.0.1", 15 | "@capacitor/ios": "^4.6.3", 16 | "@capacitor/toast": "^4.0.1", 17 | "@ionic/pwa-elements": "^3.1.1", 18 | "@ionic/react": "^6.6.1", 19 | "@ionic/react-router": "^6.6.1", 20 | "@testing-library/jest-dom": "^5.11.9", 21 | "@testing-library/react": "^13.3.0", 22 | "@testing-library/user-event": "^12.6.3", 23 | "@types/jest": "^26.0.20", 24 | "@types/node": "^12.19.15", 25 | "@types/react": "^18.0.17", 26 | "@types/react-dom": "^18.0.6", 27 | "@types/react-router": "^5.1.11", 28 | "@types/react-router-dom": "^5.1.7", 29 | "ionicons": "^6.0.3", 30 | "react": "^18.2.0", 31 | "react-dom": "^18.2.0", 32 | "react-modal": "^3.15.1", 33 | "react-router": "^5.2.0", 34 | "react-router-dom": "^5.2.0", 35 | "react-scripts": "^5.0.1", 36 | "react-sqlite-hook": "^3.2.0", 37 | "typescript": "^4.4.4", 38 | "web-vitals": "^0.2.4", 39 | "workbox-background-sync": "^5.1.4", 40 | "workbox-broadcast-update": "^5.1.4", 41 | "workbox-cacheable-response": "^5.1.4", 42 | "workbox-core": "^5.1.4", 43 | "workbox-expiration": "^5.1.4", 44 | "workbox-google-analytics": "^5.1.4", 45 | "workbox-navigation-preload": "^5.1.4", 46 | "workbox-precaching": "^5.1.4", 47 | "workbox-range-requests": "^5.1.4", 48 | "workbox-routing": "^5.1.4", 49 | "workbox-strategies": "^5.1.4", 50 | "workbox-streams": "^5.1.4" 51 | }, 52 | "scripts": { 53 | "update-jeep": "npm install --save @capacitor-community/sqlite@latest --save-dev react-sqlite-hook@latest", 54 | "update-capacitor": "npm install --save @capacitor/core@latest --save @capacitor/android@latest --save @capacitor/ios@latest --save-dev @capacitor/cli@latest", 55 | "start": "npm run copysqlwasm && react-scripts start", 56 | "build": "npm run copysqlwasm && react-scripts build", 57 | "test": "react-scripts test --transformIgnorePatterns 'node_modules/(?!(@ionic/react|@ionic/react-router|@ionic/core|@stencil/core|ionicons)/)'", 58 | "eject": "react-scripts eject", 59 | "copysqlwasm": "copyfiles -u 3 node_modules/sql.js/dist/sql-wasm.wasm public/assets" 60 | }, 61 | "eslintConfig": { 62 | "extends": [ 63 | "react-app", 64 | "react-app/jest" 65 | ] 66 | }, 67 | "browserslist": { 68 | "production": [ 69 | ">0.2%", 70 | "not dead", 71 | "not op_mini all" 72 | ], 73 | "development": [ 74 | "last 1 chrome version", 75 | "last 1 firefox version", 76 | "last 1 safari version" 77 | ] 78 | }, 79 | "devDependencies": { 80 | "@capacitor/cli": "^4.6.3", 81 | "@types/react-modal": "^3.13.1", 82 | "copyfiles": "^2.4.1" 83 | }, 84 | "repository": { 85 | "type": "git", 86 | "url": "https://github.com/jepiqueau/react-sqlite-app-starter.git" 87 | }, 88 | "bugs": { 89 | "url": "https://github.com/jepiqueau/react-sqlite-app-starter.git/issues" 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /public/assets/databases/databases.json: -------------------------------------------------------------------------------- 1 | { 2 | "databaseList" : [ 3 | "dbForCopy.db", 4 | "myDBSQLite.db" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /public/assets/databases/dbForCopy.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/public/assets/databases/dbForCopy.db -------------------------------------------------------------------------------- /public/assets/databases/myDBSQLite.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/public/assets/databases/myDBSQLite.db -------------------------------------------------------------------------------- /public/assets/icon/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/public/assets/icon/favicon.png -------------------------------------------------------------------------------- /public/assets/icon/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/public/assets/icon/icon.png -------------------------------------------------------------------------------- /public/assets/shapes.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/assets/sql-wasm.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/public/assets/sql-wasm.wasm -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Ionic App 6 | 7 | 8 | 9 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Ionic App", 3 | "name": "My Ionic App", 4 | "icons": [ 5 | { 6 | "src": "assets/icon/favicon.png", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "assets/icon/icon.png", 12 | "type": "image/png", 13 | "sizes": "512x512", 14 | "purpose": "maskable" 15 | } 16 | ], 17 | "start_url": ".", 18 | "display": "standalone", 19 | "theme_color": "#ffffff", 20 | "background_color": "#ffffff" 21 | } 22 | -------------------------------------------------------------------------------- /src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders without crashing', () => { 6 | const { baseElement } = render(); 7 | expect(baseElement).toBeDefined(); 8 | }); 9 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef } from 'react'; 2 | import { Redirect, Route } from 'react-router-dom'; 3 | import { 4 | IonApp, 5 | IonIcon, 6 | IonLabel, 7 | IonRouterOutlet, 8 | IonTabBar, 9 | IonTabButton, 10 | IonTabs, 11 | setupIonicReact, 12 | useIonModal 13 | } from '@ionic/react'; 14 | import { IonReactRouter } from '@ionic/react-router'; 15 | import { ellipse, square, triangle } from 'ionicons/icons'; 16 | import Tab1 from './pages/Tab1'; 17 | import Tab2 from './pages/Tab2'; 18 | import Tab3 from './pages/Tab3'; 19 | import ViewTest from './pages/ViewTest'; 20 | import { SQLiteHook, useSQLite } from 'react-sqlite-hook'; 21 | import ViewMessage from './pages/ViewMessage'; 22 | 23 | /* Core CSS required for Ionic components to work properly */ 24 | import '@ionic/react/css/core.css'; 25 | 26 | /* Basic CSS for apps built with Ionic */ 27 | import '@ionic/react/css/normalize.css'; 28 | import '@ionic/react/css/structure.css'; 29 | import '@ionic/react/css/typography.css'; 30 | 31 | /* Optional CSS utils that can be commented out */ 32 | import '@ionic/react/css/padding.css'; 33 | import '@ionic/react/css/float-elements.css'; 34 | import '@ionic/react/css/text-alignment.css'; 35 | import '@ionic/react/css/text-transformation.css'; 36 | import '@ionic/react/css/flex-utils.css'; 37 | import '@ionic/react/css/display.css'; 38 | 39 | /* Theme variables */ 40 | import './theme/variables.css'; 41 | 42 | interface JsonListenerInterface { 43 | jsonListeners: boolean, 44 | setJsonListeners: React.Dispatch>, 45 | } 46 | interface existingConnInterface { 47 | existConn: boolean, 48 | setExistConn: React.Dispatch>, 49 | } 50 | 51 | // Singleton SQLite Hook 52 | export let sqlite: SQLiteHook; 53 | // Existing Connections Store 54 | export let existingConn: existingConnInterface; 55 | // Is Json Listeners used 56 | export let isJsonListeners: JsonListenerInterface; 57 | 58 | setupIonicReact(); 59 | 60 | const App: React.FC = () => { 61 | const [existConn, setExistConn] = useState(false); 62 | existingConn = {existConn: existConn, setExistConn: setExistConn}; 63 | 64 | // !!!!! if you do not want to use the progress events !!!!! 65 | // since react-sqlite-hook 2.1.0 66 | // sqlite = useSQLite() 67 | // before 68 | // sqlite = useSQLite({}) 69 | // !!!!! !!!!! 70 | 71 | sqlite = useSQLite(); 72 | console.log(`$$$ in App sqlite.isAvailable ${sqlite.isAvailable} $$$`); 73 | 74 | 75 | return ( 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | Tab 1 100 | 101 | 102 | 103 | Tab 2 104 | 105 | 106 | 107 | Tab 3 108 | 109 | 110 | 111 | 112 | 113 | ); 114 | }; 115 | 116 | export default App; 117 | -------------------------------------------------------------------------------- /src/Utils/base64Images.ts: -------------------------------------------------------------------------------- 1 | export const Images: Array = [ 2 | "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAkCAYAAAD7PHgWAAAEcElEQVRYR8WYP2hTQRzHfx10aQchi0JcLGpBSBcrlTrpIjoFiy6FDipOHVz8Q0HrUGxdg1N1KBRBackiVoQ6FMVIuzQgpEpdjOiSLUXQIfK9976X37t3l6RNxVuS3Hvv7nPf3+/3vcvraTQaDdlFK4z3yMT8rh7d0Ww97QAzfX12wFq9br4buOk7UpicaQm5F4toCajh9LKnLm23Bex0Ee3k7ArwS/mVvH5elqEzzWmGr0dhDwGGFs3ouMAdA7491y+Dhw5KZuG9UEEA1r6XZfhUPOxgQ0pzPQJIDTi11NtOKOkKkHCcpfDrjQlxaXnGdFE1fAcg2to7sWmgAfVYWCzbPwO06imNHt0Tyd/IyfDlrYRy7kI3fvyUsyvRPbsCxIPIGQ6MAdFWD5RbKnjxZhTSWn0+AqyuS2agEPWNjZhPjrUngBgQkABDQ3hNOJdnmvkXa5UZ6W2CxXBaRoBiLLR2cLgnUSRIbOSLlptVx8LQk7k5iHutah44Pks12+VfApBVh04YsAbV1yR7sslYXU+oSPUK46NWZWPmseJdATLfTJ5UJsxYBNXqoc+EeX7RgpbmRmX1pcjsSq95VkP5AM1czMl63ViS27iNen2QYSUoH+bWVq1WpTh5OAFp1ekbtz7JRVJBPH/+Sk6O5i4YQCxc57Sbq0i1loA2R6hKfDho7rFLqZWzYvXiqCKgSi/6LSC+o7l2ZCIWz5UChHqfH2alvPVVRp/sT4Q7P/1NstmssZ6okNKAyD803+5BICjohjm90qgnAajhcNEHiP7BgQHZqFQkK49FF40uDtyHrZAKEQ6/NWDIoAkcBAQcmpuHoZWG+l1IwlHBjgGp3rP1zchi4kpG3vi+7wQUkMgz5p8tKIwdnzHbhtiatALTRcLvtBnmmc/ANQCuo3JxLGMF6+tmHFUULqgJsUl6Bwy/jXr1elQUWlGnj37JyfQksBhWL/tpM/itK9kHanOQ3rd47bcZxxSIkl97ow67u2Lfouh/+l6EnIvXuU5/TNkMAAjnA7RhUf9RQkWkTRhh9TUCuuO6kUooCMBc/xHzzLG71ZYJjAUhPD6TDUERxoXTC7CRiqOXAIRBZ/J5e3/oXxvhdE6FqpA2g+sslFaA3iLRMmvfYz6l8ixWD/3adF0bwXUNiN87gcP9qfOg72jkepVWkIC6ELQZu5BdAWIwbSl6F9AWQEAXRB8GtOpaxa4BCan3Tp3cemJ3G9R+R/g9DbGenDtLCJQVHIL0AeqKb7fFkaWjdzMIrz4+afdvpWKoslks+Lx9YltufQy/hPICUj1OQAOHR9KGeABwAfk6xOeFOmdrxaI5c6Ktffgjs5/4VzV6QRVUkKcafRMHQh8hQ9udPrm4ChJQw7n3EJYp4D0PPl3YlKtjx+0K3UEAiZ3G9T3fATWRd5UJ8cEBCm3o9D47Fc8CKUCEEw/om/kUD7H4zY2e+Vh8UJb8/fTrDt+BA8/rfZ/j63m9gLSYUHL7Ks99ndZpdYZew3Fub4hbVd3/uvYXfqiMwjPten8AAAAASUVORK5CYII=", 3 | "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAU1QTFRFNjtAQEVK////bG9zSk9T/v7+/f39/f3+9vf3O0BETlJWNzxB/Pz8d3t+TFFVzM3O1NXX7u/vUldbRElNs7W3v8HCmZyeRkpPW19j8vLy7u7vvsDC9PT1cHR3Oj9Eo6WnxsjJR0tQOD1Bj5KVgYSHTVFWtri50dLUtLa4YmZqOT5D8vPzRUpOkZOWc3Z64uPjr7Gzuru95+jpX2NnaGxwPkNHp6mrioyPlZeadXh8Q0hNPEBFyszNh4qNc3d6eHx/OD1Cw8XGXGBkfoGEra+xxcbIgoaJu72/m52ggoWIZ2tu8/P0wcLE+vr7kZSXgIOGP0NIvr/BvL6/QUZKP0RJkpWYpKaoqKqtVVldmJqdl5qcZWhstbe5bHB0bnJ1UVVZwsTF5ubnT1RYcHN3oaSm3N3e3NzdQkdLnJ+h9fX1TlNX+Pj47/DwwsPFVFhcEpC44wAAAShJREFUeNq8k0VvxDAQhZOXDS52mRnKzLRlZmZm+v/HxmnUOlFaSz3su4xm/BkGzLn4P+XimOJZyw0FKufelfbfAe89dMmBBdUZ8G1eCJMba69Al+AABOOm/7j0DDGXtQP9bXjYN2tWGQfyA1Yg1kSu95x9GKHiIOBXLcAwUD1JJSBVfUbwGGi2AIvoneK4bCblSS8b0RwwRAPbCHx52kH60K1b9zQUjQKiULbMDbulEjGha/RQQFDE0/ezW8kR3C3kOJXmFcSyrcQR7FDAi55nuGABZkT5hqpk3xughDN7FOHHHd0LLU9qtV7r7uhsuRwt6pEJJFVLN4V5CT+SErpXt81DbHautkpBeHeaqNDRqUA0Uo5GkgXGyI3xDZ/q/wJMsb7/pwADAGqZHDyWkHd1AAAAAElFTkSuQmCC" 4 | ]; -------------------------------------------------------------------------------- /src/Utils/deleteDBUtil.ts: -------------------------------------------------------------------------------- 1 | import { SQLiteDBConnection } from '@capacitor-community/sqlite'; 2 | 3 | export async function deleteDatabase(db: SQLiteDBConnection): Promise { 4 | try { 5 | let ret: any = await db.isExists(); 6 | if(ret.result) { 7 | const dbName = db.getConnectionDBName(); 8 | console.log("$$$ database " + dbName + " before delete"); 9 | await db.delete(); 10 | console.log("$$$ database " + dbName + " after delete " + ret.result); 11 | return Promise.resolve(); 12 | } else { 13 | return Promise.resolve(); 14 | } 15 | } catch (err) { 16 | return Promise.reject(err); 17 | } 18 | } -------------------------------------------------------------------------------- /src/Utils/encryptedSetUtils.ts: -------------------------------------------------------------------------------- 1 | import { capSQLiteSet } from '@capacitor-community/sqlite'; 2 | export const createSchemaContacts: string = ` 3 | CREATE TABLE IF NOT EXISTS contacts ( 4 | id INTEGER PRIMARY KEY NOT NULL, 5 | email TEXT UNIQUE NOT NULL, 6 | name TEXT, 7 | FirstName TEXT, 8 | company TEXT, 9 | size REAL, 10 | age INTEGER, 11 | MobileNumber TEXT, 12 | sql_deleted BOOLEAN DEFAULT 0 CHECK (sql_deleted IN (0, 1)), 13 | last_modified INTEGER DEFAULT (strftime('%s', 'now')) 14 | ); 15 | CREATE INDEX IF NOT EXISTS contacts_index_name ON contacts (name); 16 | CREATE INDEX IF NOT EXISTS contacts_index_email ON contacts (email); 17 | PRAGMA user_version = 1; 18 | `; 19 | export const setContacts: Array = [ 20 | { statement:"INSERT INTO contacts (name,FirstName,email,age,MobileNumber) VALUES (?,?,?,?,?);", 21 | values:["Simpson","Tom","Simpson@example.com",69,"4405060708"] 22 | }, 23 | { statement:"INSERT INTO contacts (name,FirstName,email,age,MobileNumber) VALUES (?,?,?,?,?);", 24 | values:["Jones","David","Jones@example.com",42,"4404030201"] 25 | }, 26 | { statement:"INSERT INTO contacts (name,FirstName,email,age,MobileNumber) VALUES (?,?,?,?,?);", 27 | values:["Whiteley","Dave","Whiteley@example.com",45,"4405162732"] 28 | }, 29 | { statement:"INSERT INTO contacts (name,FirstName,email,age,MobileNumber) VALUES (?,?,?,?,?);", 30 | values:["Brown","John","Brown@example.com",35,"4405243853"] 31 | }, 32 | { statement:"UPDATE contacts SET age = ? , MobileNumber = ? WHERE id = ?;", 33 | values:[51,"4404030202",2] 34 | } 35 | ]; 36 | export const createSchemaMessages: string = ` 37 | CREATE TABLE IF NOT EXISTS messages ( 38 | id INTEGER PRIMARY KEY NOT NULL, 39 | contactid INTEGER, 40 | title TEXT NOT NULL, 41 | body TEXT NOT NULL, 42 | sql_deleted BOOLEAN DEFAULT 0 CHECK (sql_deleted IN (0, 1)), 43 | last_modified INTEGER DEFAULT (strftime('%s', 'now')), 44 | FOREIGN KEY (contactid) REFERENCES contacts(id) ON DELETE SET DEFAULT 45 | ); 46 | CREATE INDEX IF NOT EXISTS messages_index_name ON messages (title); 47 | CREATE INDEX IF NOT EXISTS messages_index_last_modified ON messages (last_modified); 48 | ` 49 | export const setMessages: Array = [ 50 | { statement:"INSERT INTO messages (contactid,title,body) VALUES (?,?,?);", 51 | values:[1,"message 1","body message1"] 52 | }, 53 | { statement:"INSERT INTO messages (contactid,title,body) VALUES (?,?,?);", 54 | values:[2,"message 2","body message2"] 55 | }, 56 | { statement:"INSERT INTO messages (contactid,title,body) VALUES (?,?,?);", 57 | values:[1,"message 3","body message3"] 58 | }, 59 | ] -------------------------------------------------------------------------------- /src/Utils/noEncryptionUtils.ts: -------------------------------------------------------------------------------- 1 | import { capSQLiteSet } from '@capacitor-community/sqlite'; 2 | export const createTablesNoEncryption: string = `-- testcomments 3 | CREATE TABLE IF NOT EXISTS users ( 4 | /* 5 | * Author: jeepq 6 | * Purpose: To show a comment that spans multiple lines in your SQL 7 | * statement in SQLite. 8 | */ 9 | id INTEGER PRIMARY KEY NOT NULL, 10 | email TEXT UNIQUE NOT NULL, -- email as key 11 | name TEXT, 12 | company TEXT, 13 | size FLOAT, 14 | age INTEGER, 15 | sql_deleted BOOLEAN DEFAULT 0 CHECK (sql_deleted IN (0, 1)), 16 | last_modified INTEGER DEFAULT (strftime('%s', 'now')) 17 | ); 18 | `; 19 | export const createTablesNoEncryption1: string = ` 20 | CREATE TABLE IF NOT EXISTS users ( 21 | id INTEGER PRIMARY KEY NOT NULL, 22 | email TEXT UNIQUE NOT NULL, 23 | name TEXT, 24 | company TEXT, 25 | size FLOAT, 26 | age INTEGER, 27 | sql_deleted BOOLEAN DEFAULT 0 CHECK (sql_deleted IN (0, 1)), 28 | last_modified INTEGER DEFAULT (strftime('%s', 'now')) 29 | ); 30 | CREATE TABLE IF NOT EXISTS messages ( 31 | id INTEGER PRIMARY KEY NOT NULL, 32 | userid INTEGER, 33 | title TEXT NOT NULL, 34 | body TEXT NOT NULL, 35 | sql_deleted BOOLEAN DEFAULT 0 CHECK (sql_deleted IN (0, 1)), 36 | last_modified INTEGER DEFAULT (strftime('%s', 'now')), 37 | FOREIGN KEY (userid) REFERENCES users(id) ON DELETE SET DEFAULT 38 | ); 39 | CREATE INDEX IF NOT EXISTS users_index_name ON users (name); 40 | CREATE INDEX IF NOT EXISTS users_index_last_modified ON users (last_modified); 41 | CREATE INDEX IF NOT EXISTS messages_index_last_modified ON messages (last_modified); 42 | CREATE TRIGGER IF NOT EXISTS users_trigger_last_modified 43 | AFTER UPDATE ON users 44 | FOR EACH ROW WHEN NEW.last_modified < OLD.last_modified 45 | BEGIN 46 | UPDATE users SET last_modified= (strftime('%s', 'now')) WHERE id=OLD.id; 47 | END; 48 | CREATE TRIGGER IF NOT EXISTS messages_trigger_last_modified AFTER UPDATE ON messages 49 | FOR EACH ROW WHEN NEW.last_modified < OLD.last_modified 50 | BEGIN 51 | UPDATE messages SET last_modified= (strftime('%s', 'now')) WHERE id=OLD.id; 52 | END; 53 | PRAGMA user_version = 1; 54 | `; 55 | export const importTwoUsers: string = ` 56 | DELETE FROM users; 57 | INSERT INTO users (name,email,age) VALUES ("Whiteley","Whiteley.com",30); 58 | INSERT INTO users (name,email,age) VALUES ("Jones","Jones.com",44); 59 | `; 60 | export const importThreeMessages: string = ` 61 | DELETE FROM messages; 62 | INSERT INTO messages (userid,title,body) VALUES (1,"test post 1","content test post 1"); 63 | INSERT INTO messages (userid,title,body) VALUES (2,"test post 2","content test post 2"); 64 | INSERT INTO messages (userid,title,body) VALUES (1,"test post 3","content test post 3"); 65 | `; 66 | export const dropTablesTablesNoEncryption: string = ` 67 | PRAGMA foreign_keys = OFF; 68 | DROP TABLE IF EXISTS users; 69 | DROP TABLE IF EXISTS messages; 70 | PRAGMA foreign_keys = ON; 71 | `; 72 | 73 | export const setUsers: Array = [ 74 | { statement:"INSERT INTO users (name,email,age) VALUES (?,?,?);", 75 | values:["Jackson","Jackson@example.com",18] 76 | }, 77 | { statement:"INSERT INTO users (name,email,age) VALUES (?,?,?);", 78 | values:["Kennedy","Kennedy@example.com",25] 79 | }, 80 | { statement:"INSERT INTO users (name,email,age) VALUES (?,?,?);", 81 | values:["Bush","Bush@example.com",42] 82 | }, 83 | ]; -------------------------------------------------------------------------------- /src/Utils/upgrade-database-version.ts: -------------------------------------------------------------------------------- 1 | export const versionUpgrades = [ 2 | { 3 | toVersion: 1, 4 | statements: [ 5 | `CREATE TABLE IF NOT EXISTS users ( 6 | id INTEGER PRIMARY KEY NOT NULL, 7 | email TEXT UNIQUE NOT NULL, 8 | name TEXT, 9 | company TEXT, 10 | size REAL, 11 | age INTEGER, 12 | last_modified INTEGER DEFAULT (strftime('%s', 'now')) 13 | );`, 14 | `CREATE INDEX IF NOT EXISTS users_index_name ON users (name);`, 15 | `CREATE INDEX IF NOT EXISTS users_index_last_modified ON users (last_modified);`, 16 | `CREATE TRIGGER IF NOT EXISTS users_trigger_last_modified 17 | AFTER UPDATE ON users 18 | FOR EACH ROW WHEN NEW.last_modified < OLD.last_modified 19 | BEGIN 20 | UPDATE users SET last_modified= (strftime('%s', 'now')) WHERE id=OLD.id; 21 | END;`, 22 | `INSERT INTO users (name,email,age) VALUES ('Whiteley', 'whiteley@local.host', 30);`, 23 | `INSERT INTO users (name,email,age) VALUES ('Jones', 'jones@local.host', 44);`, 24 | ] 25 | }, 26 | { 27 | toVersion: 2, 28 | statements: [ 29 | `ALTER TABLE users ADD COLUMN country TEXT;`, 30 | `ALTER TABLE users ADD COLUMN sql_deleted BOOLEAN DEFAULT 0 CHECK (sql_deleted IN (0, 1));`, 31 | `CREATE TABLE messages ( 32 | id INTEGER PRIMARY KEY NOT NULL, 33 | userid INTEGER, 34 | title TEXT NOT NULL, 35 | body TEXT NOT NULL, 36 | sql_deleted BOOLEAN DEFAULT 0 CHECK (sql_deleted IN (0, 1)), 37 | last_modified INTEGER DEFAULT (strftime('%s', 'now')), 38 | FOREIGN KEY (userid) REFERENCES users(id) ON DELETE SET DEFAULT 39 | );`, 40 | `CREATE INDEX messages_index_title ON messages (title);`, 41 | `CREATE INDEX messages_index_last_modified ON messages (last_modified);`, 42 | `INSERT INTO messages (userid,title,body) VALUES (1,'test message 1','content test message 1');`, 43 | `INSERT INTO messages (userid,title,body) VALUES (2,'test message 2','content test message 2');`, 44 | `INSERT INTO messages (userid,title,body) VALUES (1,'test message 3','content test message 3');`, 45 | `UPDATE users SET country = 'United Kingdom' WHERE id = 1;`, 46 | `UPDATE users SET country = 'Australia' WHERE id = 2;`, 47 | ] 48 | } 49 | ] -------------------------------------------------------------------------------- /src/Utils/upgradeVersionUtils.ts: -------------------------------------------------------------------------------- 1 | 2 | export const userMessages = ` 3 | SELECT users.name,messages.title,messages.body FROM users 4 | INNER JOIN messages ON users.id = messages.userid 5 | WHERE users.id = ?; 6 | `; -------------------------------------------------------------------------------- /src/components/CopyFromAssets.css: -------------------------------------------------------------------------------- 1 | .container-copyfromassets { 2 | text-align: left; 3 | position: relative; 4 | left: 2px; 5 | right: 2px; 6 | top: 10px; 7 | } -------------------------------------------------------------------------------- /src/components/CopyFromAssets.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from 'react'; 2 | import './CopyFromAssets.css'; 3 | import TestOutput from './TestOutput'; 4 | 5 | import { sqlite } from '../App'; 6 | import { SQLiteDBConnection } from 'react-sqlite-hook'; 7 | import { Dialog } from '@capacitor/dialog'; 8 | 9 | const CopyFromAssets: React.FC = () => { 10 | const myRef = useRef(false); 11 | const myLog: string[] = []; 12 | const errMess = useRef(""); 13 | const [output, setOutput] = useState({log: myLog}); 14 | const showAlert = async (message: string) => { 15 | await Dialog.alert({ 16 | title: 'Error Dialog', 17 | message: message, 18 | }); 19 | }; 20 | const testDatabaseCopyFromAssets = async (): Promise => { 21 | setOutput((output: { log: any; }) => ({log: output.log})); 22 | 23 | myLog.push("* Starting testDatabaseCopyFromAssets *\n"); 24 | try { 25 | 26 | await sqlite.copyFromAssets(); 27 | myLog.push("> copyFromAssets successful\n"); 28 | 29 | // create a connection for myDB 30 | let db: SQLiteDBConnection = await sqlite.createConnection("myDB"); 31 | myLog.push("> createConnection " + 32 | " 'myDb' successful\n"); 33 | await db.open(); 34 | myLog.push("> open 'myDb' successful\n"); 35 | // Select all Users 36 | let res: any = await db.query("SELECT * FROM users"); 37 | console.log(`@@@ res.values.length ${res.values.length}`) 38 | if(res.values.length !== 7 || 39 | res.values[0].name !== "Whiteley" || 40 | res.values[1].name !== "Jones" || 41 | res.values[2].name !== "Simpson" || 42 | res.values[3].name !== "Brown" || 43 | res.values[4].name !== "Jackson" || 44 | res.values[5].name !== "Kennedy" || 45 | res.values[6].name !== "Bush") { 46 | errMess.current = `Query not returning 7 values`; 47 | return false; 48 | } 49 | 50 | myLog.push("> query 'myDb' successful\n"); 51 | 52 | 53 | // Close Connection MyDB 54 | await sqlite.closeConnection("myDB"); 55 | myLog.push("> closeConnection 'myDb' successful\n"); 56 | 57 | // create a connection for dbForCopy 58 | db = await sqlite.createConnection("dbForCopy"); 59 | myLog.push("> createConnection " + 60 | " 'dbForCopy' successful\n"); 61 | await db.open(); 62 | myLog.push("> open 'dbForCopy' successful\n"); 63 | // Select all Users 64 | res = await db.query("SELECT * FROM areas"); 65 | console.log(`@@@ res.values.length ${res.values.length}`) 66 | if(res.values.length !== 3 || 67 | res.values[0].name !== "Access road" || 68 | res.values[1].name !== "Accessway" || 69 | res.values[2].name !== "Air handling system"){ 70 | errMess.current = `Query Users not returning 3 values`; 71 | setOutput(() => ({log: myLog})); 72 | return false; 73 | } 74 | 75 | myLog.push("> query 'dbForCopy' successful\n"); 76 | // Close Connection dbForCopy 77 | await sqlite.closeConnection("dbForCopy"); 78 | myLog.push("> closeConnection 'dbForCopy' successful\n"); 79 | 80 | myLog.push("* Ending testDatabaseCopyFromAssets *\n"); 81 | 82 | return true; 83 | } catch (err: any) { 84 | errMess.current = `${err.message}`; 85 | setOutput(() => ({log: myLog})); 86 | return false; 87 | } 88 | } 89 | 90 | useEffect( () => { 91 | if(sqlite.isAvailable) { 92 | if (myRef.current === false) { 93 | myRef.current = true; 94 | testDatabaseCopyFromAssets().then(async res => { 95 | if(res) { 96 | myLog.push("\n* The set of tests was successful *\n"); 97 | } else { 98 | myLog.push("\n* The set of tests failed *\n"); 99 | await showAlert(errMess.current); 100 | } 101 | setOutput(() => ({log: myLog})); 102 | }); 103 | } 104 | } else { 105 | sqlite.getPlatform().then(async (ret: { platform: string; }) => { 106 | myLog.push("\n* Not available for " + 107 | ret.platform + " platform *\n"); 108 | await showAlert(errMess.current); 109 | setOutput(() => ({log: myLog})); 110 | }); 111 | } 112 | 113 | }); 114 | 115 | return ( 116 | 117 | ); 118 | }; 119 | 120 | export default CopyFromAssets; 121 | -------------------------------------------------------------------------------- /src/components/ExistingConnection.css: -------------------------------------------------------------------------------- 1 | .container-existingconnection { 2 | text-align: left; 3 | position: relative; 4 | left: 2px; 5 | right: 2px; 6 | top: 10px; 7 | } -------------------------------------------------------------------------------- /src/components/ExistingConnection.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from 'react'; 2 | import './ExistingConnection.css'; 3 | import TestOutput from './TestOutput'; 4 | 5 | import { setUsers } from '../Utils/noEncryptionUtils'; 6 | import { createSchemaMessages, setMessages } from '../Utils/encryptedSetUtils'; 7 | 8 | import { sqlite, existingConn } from '../App'; 9 | import { Dialog } from '@capacitor/dialog'; 10 | 11 | const ExistingConnection: React.FC = () => { 12 | const myRef = useRef(false); 13 | const myLog: string[] = []; 14 | const errMess = useRef(""); 15 | const [output, setOutput] = useState({log: myLog}); 16 | const showAlert = async (message: string) => { 17 | await Dialog.alert({ 18 | title: 'Error Dialog', 19 | message: message, 20 | }); 21 | }; 22 | const testExistingConns = async (): Promise => { 23 | setOutput((output: { log: any; }) => ({log: output.log})); 24 | 25 | myLog.push("* Starting testExistingConns *\n"); 26 | try { 27 | // retrieve the connections 28 | const db = await sqlite.retrieveConnection("testNew") 29 | const db1 = await sqlite.retrieveConnection("testSet") 30 | 31 | // load setUsers in db 32 | var ret: any = await db.executeSet(setUsers); 33 | console.log('$$$ ret.changes.changes in db ' + ret.changes.changes) 34 | if (ret.changes.changes !== 3) { 35 | errMess.current = `ExecuteSet setUsers changes != 3`; 36 | setOutput(() => ({log: myLog})); 37 | return false; 38 | } 39 | // select all users in db 40 | ret = await db.query("SELECT * FROM users;"); 41 | if(ret.values.length !== 7 || ret.values[0].name !== "Whiteley" || 42 | ret.values[1].name !== "Jones" || 43 | ret.values[2].name !== "Simpson" || 44 | ret.values[3].name !== "Brown" || 45 | ret.values[4].name !== "Jackson" || 46 | ret.values[5].name !== "Kennedy" || 47 | ret.values[6].name !== "Bush" 48 | ) { 49 | errMess.current = `Query users not returning 7 values`; 50 | setOutput(() => ({log: myLog})); 51 | return false; 52 | } 53 | 54 | // create table messages in db1 55 | ret = await db1.execute(createSchemaMessages); 56 | console.log('$$$ ret.changes.changes in db1 ' + ret.changes.changes) 57 | if (ret.changes.changes < 0) { 58 | errMess.current = `Execute createSchemaMessages changes < 0`; 59 | setOutput(() => ({log: myLog})); 60 | return false; 61 | } 62 | 63 | // load setMessages in db1 64 | ret = await db1.executeSet(setMessages); 65 | console.log('$$$ ret.changes.changes in db1 ' + ret.changes.changes) 66 | if (ret.changes.changes !== 3) { 67 | errMess.current = `ExecuteSet setMessages changes < 0`; 68 | setOutput(() => ({log: myLog})); 69 | return false; 70 | } 71 | // select all users in db 72 | ret = await db1.query("SELECT * FROM messages;"); 73 | if(ret.values.length !== 3 || ret.values[0].title !== "message 1" || 74 | ret.values[1].title !== "message 2" || 75 | ret.values[2].title !== "message 3" 76 | ) { 77 | errMess.current = `Query messages not returning 3 values`; 78 | setOutput(() => ({log: myLog})); 79 | return false; 80 | } 81 | 82 | // test retrieve all connections 83 | var retDict: Map = await 84 | sqlite.retrieveAllConnections(); 85 | if(!retDict.has("RW_testNew") || retDict.get("RW_testNew") !== db) { 86 | errMess.current = `retrieveAllConnections not returning "testNew"`; 87 | setOutput(() => ({log: myLog})); 88 | return false; 89 | } 90 | if(!retDict.has("RW_testSet") || retDict.get("RW_testSet") !== db1) { 91 | errMess.current = `retrieveAllConnections not returning "testSet"`; 92 | setOutput(() => ({log: myLog})); 93 | return false; 94 | } 95 | 96 | // close all connections 97 | sqlite.closeAllConnections(); 98 | 99 | myLog.push("* Ending testExistingConns *\n"); 100 | existingConn.setExistConn(false); 101 | return true; 102 | } catch (err: any) { 103 | errMess.current = `${err.message}`; 104 | setOutput(() => ({log: myLog})); 105 | return false; 106 | } 107 | } 108 | 109 | useEffect( () => { 110 | if(sqlite.isAvailable) { 111 | if (myRef.current === false) { 112 | myRef.current = true; 113 | testExistingConns().then(async res => { 114 | if(res) { 115 | myLog.push("\n* The set of tests was successful *\n"); 116 | } else { 117 | myLog.push("\n* The set of tests failed *\n"); 118 | await showAlert(errMess.current); 119 | } 120 | setOutput(() => ({log: myLog})); 121 | }); 122 | } 123 | } else { 124 | sqlite.getPlatform().then(async (ret: { platform: string; }) => { 125 | myLog.push("\n* Not available for " + 126 | ret.platform + " platform *\n"); 127 | await showAlert(errMess.current); 128 | setOutput(() => ({log: myLog})); 129 | }); 130 | } 131 | }); 132 | 133 | return ( 134 | 135 | ); 136 | }; 137 | 138 | export default ExistingConnection; 139 | -------------------------------------------------------------------------------- /src/components/ExploreContainer.css: -------------------------------------------------------------------------------- 1 | .container { 2 | text-align: center; 3 | position: absolute; 4 | left: 0; 5 | right: 0; 6 | top: 50%; 7 | transform: translateY(-50%); 8 | } 9 | 10 | .container strong { 11 | font-size: 20px; 12 | line-height: 26px; 13 | } 14 | 15 | .container p { 16 | font-size: 16px; 17 | line-height: 22px; 18 | color: #8c8c8c; 19 | margin: 0; 20 | } 21 | 22 | .container a { 23 | text-decoration: none; 24 | } -------------------------------------------------------------------------------- /src/components/ExploreContainer.tsx: -------------------------------------------------------------------------------- 1 | import './ExploreContainer.css'; 2 | 3 | interface ContainerProps { 4 | name: string; 5 | } 6 | 7 | const ExploreContainer: React.FC = ({ name }) => { 8 | return ( 9 |
10 | {name} 11 |

Explore UI Components

12 |
13 | ); 14 | }; 15 | 16 | export default ExploreContainer; 17 | -------------------------------------------------------------------------------- /src/components/MessageListItem.css: -------------------------------------------------------------------------------- 1 | ion-item { 2 | --padding-start: 0; 3 | --inner-padding-end: 0; 4 | } 5 | 6 | ion-label { 7 | margin-top: 12px; 8 | margin-bottom: 12px; 9 | } 10 | 11 | ion-item h2 { 12 | font-weight: 600; 13 | margin: 0; 14 | } 15 | 16 | ion-item p { 17 | text-overflow: ellipsis; 18 | overflow: hidden; 19 | white-space: nowrap; 20 | width: 95%; 21 | } 22 | 23 | ion-item .date { 24 | float: right; 25 | align-items: center; 26 | display: flex; 27 | } 28 | 29 | ion-item ion-icon { 30 | color: #c9c9ca; 31 | } 32 | 33 | ion-item ion-note { 34 | font-size: 15px; 35 | margin-right: 8px; 36 | font-weight: normal; 37 | } 38 | 39 | ion-item ion-note.md { 40 | margin-right: 14px; 41 | } 42 | 43 | .dot { 44 | display: block; 45 | height: 12px; 46 | width: 12px; 47 | border-radius: 50%; 48 | align-self: start; 49 | margin: 16px 10px 16px 16px; 50 | } 51 | 52 | .dot-unread { 53 | background: var(--ion-color-primary); 54 | } 55 | 56 | ion-footer ion-title { 57 | font-size: 11px; 58 | font-weight: normal; 59 | } -------------------------------------------------------------------------------- /src/components/MessageListItem.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | IonItem, 3 | IonLabel, 4 | IonNote 5 | } from '@ionic/react'; 6 | import { Message } from '../data/messages'; 7 | import './MessageListItem.css'; 8 | 9 | interface MessageListItemProps { 10 | message: Message; 11 | } 12 | 13 | const MessageListItem: React.FC = ({ message }) => { 14 | return ( 15 | 16 |
17 | 18 |

19 | {message.fromName} 20 | 21 | {message.date} 22 | 23 |

24 |

{message.subject}

25 |

26 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 27 |

28 |
29 |
30 | ); 31 | }; 32 | 33 | export default MessageListItem; 34 | -------------------------------------------------------------------------------- /src/components/MigrateDB.css: -------------------------------------------------------------------------------- 1 | .container-migratedb { 2 | text-align: left; 3 | position: relative; 4 | left: 2px; 5 | right: 2px; 6 | top: 10px; 7 | } -------------------------------------------------------------------------------- /src/components/NoEncryption.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/src/components/NoEncryption.css -------------------------------------------------------------------------------- /src/components/NoEncryption.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useRef } from 'react'; 2 | import TestOutput from './TestOutput'; 3 | import './NoEncryption.css'; 4 | import { sqlite } from '../App'; 5 | import { SQLiteDBConnection} from 'react-sqlite-hook'; 6 | import { createTablesNoEncryption, createTablesNoEncryption1, importTwoUsers, 7 | dropTablesTablesNoEncryption } from '../Utils/noEncryptionUtils'; 8 | import { deleteDatabase } from '../Utils/deleteDBUtil'; 9 | import { Dialog } from '@capacitor/dialog'; 10 | 11 | const NoEncryption: React.FC = () => { 12 | const myRef = useRef(false); 13 | const myLog: string[] = []; 14 | const errMess = useRef(""); 15 | const [output, setOutput] = useState({log: myLog}); 16 | const showAlert = async (message: string) => { 17 | await Dialog.alert({ 18 | title: 'Error Dialog', 19 | message: message, 20 | }); 21 | }; 22 | 23 | const testDatabaseNoEncryption = async (): Promise => { 24 | setOutput((output) => ({log: output.log})); 25 | 26 | 27 | myLog.push("* Starting testDatabaseNoEncryption *\n"); 28 | try { 29 | // test the plugin with echo 30 | let res: any = await sqlite.echo("Hello from echo"); 31 | if(res.value !== "Hello from echo"){ 32 | errMess.current = `Echo not returning "Hello from echo"`; 33 | setOutput(() => ({log: myLog})); 34 | return false; 35 | } 36 | myLog.push("> Echo successful\n"); 37 | 38 | // create a connection for NoEncryption 39 | let db: SQLiteDBConnection = await sqlite.createConnection("NoEncryption"); 40 | // check if the databases exist 41 | // and delete it for multiple successive tests 42 | await deleteDatabase(db); 43 | // open NoEncryption 44 | await db.open(); 45 | myLog.push("> open 'NoEncryption' successful\n"); 46 | 47 | // Drop tables if exists 48 | res = await db.execute(dropTablesTablesNoEncryption); 49 | if(res.changes.changes !== 0 && 50 | res.changes.changes !== 1){ 51 | errMess.current = `Execute dropTablesTablesNoEncryption changes < 0`; 52 | setOutput(() => ({log: myLog})); 53 | return false; 54 | } 55 | myLog.push("> Execute1 successful\n"); 56 | 57 | // Create tables 58 | console.log(`@@@@ createTablesNoEncryption: ${createTablesNoEncryption}`); 59 | res = await db.execute(createTablesNoEncryption); 60 | if (res.changes.changes < 0) { 61 | errMess.current = `Execute createTablesNoEncryption changes < 0`; 62 | setOutput(() => ({log: myLog})); 63 | return false; 64 | } 65 | res = await db.execute(createTablesNoEncryption1); 66 | if (res.changes.changes < 0) { 67 | errMess.current = `Execute createTablesNoEncryption changes < 0`; 68 | setOutput(() => ({log: myLog})); 69 | return false; 70 | } 71 | myLog.push("> Execute2 successful\n"); 72 | 73 | // Insert two users with execute method 74 | res = await db.execute(importTwoUsers); 75 | if (res.changes.changes !== 2) { 76 | errMess.current = `Execute importTwoUsers changes != 2`; 77 | setOutput(() => ({log: myLog})); 78 | return false; 79 | } 80 | myLog.push("> Execute3 successful\n"); 81 | 82 | // Select all Users 83 | res = await db.query("SELECT * FROM users"); 84 | if(res.values.length !== 2 || 85 | res.values[0].name !== "Whiteley" || 86 | res.values[1].name !== "Jones") { 87 | errMess.current = `Query not returning 2 values`; 88 | setOutput(() => ({log: myLog})); 89 | return false; 90 | } 91 | myLog.push("> Select1 successful\n"); 92 | 93 | // add one user with statement and values 94 | let sqlcmd = "INSERT INTO users (name,email,age) VALUES (?,?,?)"; 95 | let values: Array = ["Simpson","Simpson@example.com",69]; 96 | res = await db.run(sqlcmd,values); 97 | if(res.changes.changes !== 1 || 98 | res.changes.lastId !== 3) { 99 | errMess.current = `Run lastId != 3`; 100 | setOutput(() => ({log: myLog})); 101 | return false; 102 | } 103 | myLog.push("> Run1 successful\n"); 104 | 105 | // add one user with statement 106 | sqlcmd = `INSERT INTO users (name,email,age) VALUES `+ 107 | `("Brown","Brown@example.com",15)`; 108 | res = await db.run(sqlcmd); 109 | if(res.changes.changes !== 1 || 110 | res.changes.lastId !== 4) { 111 | errMess.current = `Run lastId != 4`; 112 | setOutput(() => ({log: myLog})); 113 | return false; 114 | } 115 | myLog.push("> Run2 successful\n"); 116 | 117 | // Select all Users 118 | res = await db.query("SELECT * FROM users"); 119 | if(res.values.length !== 4) { 120 | errMess.current = `Query not returning 4 values`; 121 | setOutput(() => ({log: myLog})); 122 | return false; 123 | } 124 | myLog.push("> Select2 successful\n"); 125 | 126 | // Select Users with age > 35 127 | sqlcmd = "SELECT name,email,age FROM users WHERE age > ?"; 128 | values = ["35"]; 129 | res = await db.query(sqlcmd,values); 130 | if(res.values.length !== 2) { 131 | errMess.current = `Query > 35 not returning 2 values`; 132 | setOutput(() => ({log: myLog})); 133 | return false; 134 | } 135 | myLog.push("> Select3 successful\n"); 136 | 137 | // Close Connection NoEncryption 138 | await sqlite.closeConnection("NoEncryption"); 139 | myLog.push("> CloseConnection successful\n"); 140 | myLog.push("* Ending testDatabaseNoEncryption *\n"); 141 | 142 | 143 | return true; 144 | } catch (err: any) { 145 | errMess.current = `${err.message}`; 146 | setOutput(() => ({log: myLog})); 147 | return false; 148 | } 149 | } 150 | 151 | useEffect(() => { 152 | if(sqlite.isAvailable) { 153 | if (myRef.current === false) { 154 | myRef.current = true; 155 | 156 | testDatabaseNoEncryption().then(async res => { 157 | if(res) { 158 | myLog.push("\n* The set of tests was successful *\n"); 159 | } else { 160 | myLog.push("\n* The set of tests failed *\n"); 161 | await showAlert(errMess.current); 162 | } 163 | setOutput(() => ({log: myLog})); 164 | 165 | }); 166 | } 167 | } else { 168 | sqlite.getPlatform().then(async (ret: { platform: string; }) => { 169 | myLog.push("\n* Not available for " + 170 | ret.platform + " platform *\n"); 171 | await showAlert(errMess.current); 172 | setOutput(() => ({log: myLog})); 173 | }); 174 | } 175 | 176 | }); 177 | 178 | return ( 179 | 180 | ); 181 | }; 182 | 183 | export default NoEncryption; 184 | -------------------------------------------------------------------------------- /src/components/NonConformedDB.css: -------------------------------------------------------------------------------- 1 | .container-nonconformeddb { 2 | text-align: left; 3 | position: relative; 4 | left: 2px; 5 | right: 2px; 6 | top: 10px; 7 | } -------------------------------------------------------------------------------- /src/components/NonConformedDB.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from 'react'; 2 | import './NonConformedDB.css'; 3 | import TestOutput from './TestOutput'; 4 | import { sqlite } from '../App'; 5 | import { Dialog } from '@capacitor/dialog'; 6 | import { SQLiteDBConnection } from 'react-sqlite-hook'; 7 | 8 | const NonConformedDB: React.FC = () => { 9 | const myRef = useRef(false); 10 | const myLog: string[] = []; 11 | const errMess = useRef(""); 12 | const [output, setOutput] = useState({log: myLog}); 13 | const showAlert = async (message: string) => { 14 | await Dialog.alert({ 15 | title: 'Error Dialog', 16 | message: message, 17 | }); 18 | }; 19 | const testNonConformedDB = async (): Promise => { 20 | setOutput((output: { log: any; }) => ({log: output.log})); 21 | 22 | myLog.push("* Starting testNonConformedDB *\n"); 23 | try { 24 | const platform = (await sqlite.getPlatform()).platform; 25 | // Assuming non-conformed "testncbd.db" in the "directory" folder 26 | let directory: string = "files/databases"; 27 | if(platform === "ios") directory = "Applications/Files/Databases" 28 | if(platform === "android" ) directory = "files/databases"; 29 | if(platform === 'ios' || platform === 'android') { 30 | const databasePath = (await sqlite.getNCDatabasePath(directory,"testncdb.db")).path; 31 | if(databasePath !== undefined) { 32 | const isNCDbExists = (await sqlite.isNCDatabase(databasePath)).result; 33 | const ret = await sqlite.checkConnectionsConsistency(); 34 | const isConn = (await sqlite.isNCConnection(databasePath)).result; 35 | let db: SQLiteDBConnection 36 | if (ret.result && isConn && isNCDbExists) { 37 | db = await sqlite.retrieveNCConnection(databasePath); 38 | } else { 39 | db = await sqlite.createNCConnection(databasePath, 1); 40 | } 41 | // open db testncdb.db 42 | await db.open(); 43 | // get the database version 44 | let retVer = await db.getVersion(); 45 | if (retVer.version !== 1) { 46 | errMess.current = `GetVersion: version failed`; 47 | setOutput(() => ({log: myLog})); 48 | return false; 49 | } 50 | const isDbOpen = await db.isDBOpen(); 51 | if(!isDbOpen.result) { 52 | errMess.current = `IsDBOpen: database not opened`; 53 | setOutput(() => ({log: myLog})); 54 | return false; 55 | } 56 | // check if the table "contacts" exists 57 | const isTable = await db.isTable("contacts") 58 | if(!isTable.result) { 59 | errMess.current = `Table 'contacts' does not exist in ${databasePath}`; 60 | setOutput(() => ({log: myLog})); 61 | return false; 62 | } 63 | // select all contacts in db 64 | const retCts = await db.query("SELECT * FROM contacts;"); 65 | if(retCts !== undefined && retCts.values !== undefined) { 66 | if(retCts.values.length !== 4 || 67 | retCts.values[0].name !== "Simpson" || 68 | retCts.values[1].name !== "Jones" || 69 | retCts.values[2].name !== "Whiteley" || 70 | retCts.values[3].name !== "Brown") { 71 | errMess.current = `Query Contacts failed`; 72 | setOutput(() => ({log: myLog})); 73 | return false; 74 | } 75 | await sqlite.closeNCConnection(databasePath); 76 | } else { 77 | errMess.current = `result of Query Contacts undefined"`; 78 | setOutput(() => ({log: myLog})); 79 | return false; 80 | } 81 | } else { 82 | errMess.current = `databasePath undefined"`; 83 | setOutput(() => ({log: myLog})); 84 | return false; 85 | } 86 | } else { 87 | console.log(`Not available on ${platform} platform`); 88 | } 89 | return true; 90 | } catch (err: any) { 91 | errMess.current = `${err.message}`; 92 | setOutput(() => ({log: myLog})); 93 | return false; 94 | } 95 | } 96 | 97 | useEffect( () => { 98 | if(sqlite.isAvailable) { 99 | if (myRef.current === false) { 100 | myRef.current = true; 101 | testNonConformedDB().then(async res => { 102 | if(res) { 103 | myLog.push("\n* The set of tests was successful *\n"); 104 | } else { 105 | myLog.push("\n* The set of tests failed *\n"); 106 | await showAlert(errMess.current); 107 | } 108 | setOutput(() => ({log: myLog})); 109 | }); 110 | } 111 | } else { 112 | sqlite.getPlatform().then(async (ret: { platform: string; }) => { 113 | myLog.push("\n* Not available for " + 114 | ret.platform + " platform *\n"); 115 | await showAlert(errMess.current); 116 | setOutput(() => ({log: myLog})); 117 | }); 118 | } 119 | 120 | }); 121 | 122 | return ( 123 | 124 | );}; 125 | 126 | export default NonConformedDB; 127 | -------------------------------------------------------------------------------- /src/components/Test2dbs.css: -------------------------------------------------------------------------------- 1 | .container-test2dbs { 2 | text-align: left; 3 | position: relative; 4 | left: 2px; 5 | right: 2px; 6 | top: 10px; 7 | } -------------------------------------------------------------------------------- /src/components/Test2dbs.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from 'react'; 2 | import './Test2dbs.css'; 3 | import TestOutput from './TestOutput'; 4 | 5 | import { createTablesNoEncryption, importTwoUsers } from '../Utils/noEncryptionUtils'; 6 | import { createSchemaContacts, setContacts } from '../Utils/encryptedSetUtils'; 7 | import { deleteDatabase } from '../Utils/deleteDBUtil'; 8 | 9 | import { sqlite, existingConn } from '../App'; 10 | import { Dialog } from '@capacitor/dialog'; 11 | 12 | const Test2dbs: React.FC = () => { 13 | const myRef = useRef(false); 14 | const myLog: string[] = []; 15 | const errMess = useRef(""); 16 | const [output, setOutput] = useState({log: myLog}); 17 | const showAlert = async (message: string) => { 18 | await Dialog.alert({ 19 | title: 'Error Dialog', 20 | message: message, 21 | }); 22 | }; 23 | const testtwodbs = async (): Promise => { 24 | setOutput((output: { log: any; }) => ({log: output.log})); 25 | 26 | myLog.push("* Starting testTwoDBS *\n"); 27 | try { 28 | const platform = (await sqlite.getPlatform()).platform; 29 | // initialize the connection 30 | const db = await sqlite 31 | .createConnection("testNew", false, "no-encryption", 1); 32 | const db1 = await sqlite 33 | .createConnection("testSet", true, "secret", 1); 34 | 35 | // check if the databases exist 36 | // and delete it for multiple successive tests 37 | await deleteDatabase(db); 38 | await deleteDatabase(db1); 39 | 40 | // open db testNew 41 | await db.open(); 42 | myLog.push("> open 'testNew' successful\n"); 43 | 44 | // create tables in db 45 | let ret: any = await db.execute(createTablesNoEncryption); 46 | if (ret.changes.changes < 0) { 47 | errMess.current = `Execute changes < 0`; 48 | setOutput(() => ({log: myLog})); 49 | return false; 50 | } 51 | 52 | // create synchronization table 53 | ret = await db.createSyncTable(); 54 | if (ret.changes.changes < 0) { 55 | errMess.current = `CreateSyncTable changes < 0`; 56 | setOutput(() => ({log: myLog})); 57 | return false; 58 | } 59 | 60 | // set the synchronization date 61 | const syncDate: string = "2020-11-25T08:30:25.000Z"; 62 | await db.setSyncDate(syncDate); 63 | 64 | // add two users in db 65 | ret = await db.execute(importTwoUsers); 66 | if (ret.changes.changes !== 2) { 67 | errMess.current = `Execute importTwoUsers changes != 2`; 68 | setOutput(() => ({log: myLog})); 69 | return false; 70 | } 71 | 72 | // select all users in db 73 | ret = await db.query("SELECT * FROM users;"); 74 | 75 | if(ret.values.length !== 2 || ret.values[0].name !== "Whiteley" || 76 | ret.values[1].name !== "Jones") { 77 | errMess.current = `Query not returning 2 values`; 78 | setOutput(() => ({log: myLog})); 79 | return false; 80 | } 81 | 82 | // open db testSet 83 | await db1.open(); 84 | myLog.push("> open 'testSet' successful\n"); 85 | // create tables in db1 86 | ret = await db1.execute(createSchemaContacts); 87 | if (ret.changes.changes < 0) { 88 | errMess.current = `Execute createSchemaContacts changes < 0`; 89 | setOutput(() => ({log: myLog})); 90 | return false; 91 | } 92 | // load setContacts in db1 93 | ret = await db1.executeSet(setContacts); 94 | if (ret.changes.changes !== 5) { 95 | errMess.current = `ExecuteSet setContacts changes != 5`; 96 | setOutput(() => ({log: myLog})); 97 | return false; 98 | } 99 | 100 | // select users where company is NULL in db 101 | ret = await db.query("SELECT * FROM users WHERE company IS NULL;"); 102 | if(ret.values.length !== 2 || ret.values[0].name !== "Whiteley" || 103 | ret.values[1].name !== "Jones") { 104 | errMess.current = `Query Company is NULL not returning 2 values`; 105 | setOutput(() => ({log: myLog})); 106 | return false; 107 | } 108 | // add one user with statement and values 109 | let sqlcmd: string = 110 | "INSERT INTO users (name,email,age) VALUES (?,?,?)"; 111 | let values: Array = ["Simpson","Simpson@example.com",69]; 112 | ret = await db.run(sqlcmd,values); 113 | if(ret.changes.lastId !== 3) { 114 | errMess.current = `Run lastId != 3`; 115 | setOutput(() => ({log: myLog})); 116 | return false; 117 | } 118 | // add one user with statement 119 | sqlcmd = `INSERT INTO users (name,email,age) VALUES ` + 120 | `("Brown","Brown@example.com",15)`; 121 | ret = await db.run(sqlcmd); 122 | if(ret.changes.lastId !== 4) { 123 | errMess.current = `Run lastId != 4`; 124 | setOutput(() => ({log: myLog})); 125 | return false; 126 | } 127 | if (platform === "web") { 128 | await sqlite.saveToStore("testNew"); 129 | await sqlite.saveToStore("testSet"); 130 | } 131 | myLog.push("* Ending testTwoDBS *\n"); 132 | existingConn.setExistConn(true); 133 | return true; 134 | } catch (err:any) { 135 | errMess.current = `${err.message}`; 136 | return false; 137 | } 138 | } 139 | 140 | useEffect(() => { 141 | if(sqlite.isAvailable) { 142 | if (myRef.current === false) { 143 | myRef.current = true; 144 | 145 | testtwodbs().then(async res => { 146 | if(res) { 147 | myLog.push("\n* The set of tests was successful *\n"); 148 | } else { 149 | myLog.push("\n* The set of tests failed *\n"); 150 | await showAlert(errMess.current); 151 | } 152 | setOutput(() => ({log: myLog})); 153 | 154 | }); 155 | } 156 | } else { 157 | sqlite.getPlatform().then(async (ret: { platform: string; }) => { 158 | myLog.push("\n* Not available for " + 159 | ret.platform + " platform *\n"); 160 | await showAlert(errMess.current); 161 | setOutput(() => ({log: myLog})); 162 | }); 163 | } 164 | 165 | }); 166 | 167 | return ( 168 | 169 | ); 170 | }; 171 | 172 | export default Test2dbs; 173 | -------------------------------------------------------------------------------- /src/components/TestEncryption.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/src/components/TestEncryption.css -------------------------------------------------------------------------------- /src/components/TestIssue184.css: -------------------------------------------------------------------------------- 1 | .container-testissue184 { 2 | text-align: left; 3 | position: relative; 4 | left: 2px; 5 | right: 2px; 6 | top: 10px; 7 | } -------------------------------------------------------------------------------- /src/components/TestIssue184.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from 'react'; 2 | import './TestIssue184.css'; 3 | import TestOutput from './TestOutput'; 4 | 5 | import { sqlite } from '../App'; 6 | import { SQLiteDBConnection} from 'react-sqlite-hook'; 7 | import { Dialog } from '@capacitor/dialog'; 8 | 9 | const TestIssue184: React.FC = () => { 10 | const myRef = useRef(false); 11 | const myLog: string[] = []; 12 | const errMess = useRef(""); 13 | const [output, setOutput] = useState({log: myLog}); 14 | const showAlert = async (message: string) => { 15 | await Dialog.alert({ 16 | title: 'Error Dialog', 17 | message: message, 18 | }); 19 | }; 20 | const testDatabaseIssue184 = async (): Promise => { 21 | setOutput((output: { log: any; }) => ({log: output.log})); 22 | 23 | myLog.push("* Starting testDatabaseIssue184 *\n"); 24 | try { 25 | // create a connection for TestIssue184 26 | let db: SQLiteDBConnection = await sqlite.createConnection("steward"); 27 | // open NoEncryption 28 | await db.open(); 29 | myLog.push("> open 'steward' successful\n"); 30 | myLog.push("executing create statements on next line\n"); 31 | let res = await db.execute(` 32 | DROP TABLE IF EXISTS chores; 33 | DROP TABLE IF EXISTS items; 34 | 35 | CREATE TABLE IF NOT EXISTS chores ( 36 | id INTEGER PRIMARY KEY, 37 | name TEXT, 38 | next_due DATE, 39 | completed BOOLEAN, 40 | repeats BOOLEAN, 41 | frequency INT 42 | ); 43 | 44 | CREATE TABLE IF NOT EXISTS items ( 45 | id INTEGER PRIMARY KEY, 46 | name TEXT, 47 | image TEXT, 48 | next_buy DATE, 49 | buy_from TEXT, 50 | supplied BOOLEAN 51 | ); 52 | `); 53 | if(res.changes && res.changes.changes !== 0 && 54 | res.changes.changes !== 1){ 55 | errMess.current = `Execute create tables changes < 0`; 56 | setOutput(() => ({log: myLog})); 57 | return false; 58 | } 59 | myLog.push(" executed create statements\n"); 60 | 61 | myLog.push(" executing insert on next line\n"); 62 | 63 | res = await db.execute( 64 | "INSERT INTO chores (name,next_due,completed,repeats,frequency) VALUES ('Clean room 2', '2021-12-11', false, false, 1);" 65 | ); 66 | if (res.changes && res.changes.changes !== 1) { 67 | errMess.current = `Execute Insert changes != 1`; 68 | setOutput(() => ({log: myLog})); 69 | return false; 70 | } 71 | myLog.push(" executed insert\n"); 72 | 73 | // Select all chores 74 | myLog.push(" selecting from table\n"); 75 | const resVal = await db.query("SELECT * FROM chores;"); 76 | if(resVal.values && resVal.values.length !== 1) { 77 | errMess.current = `Query not returning 1 values`; 78 | setOutput(() => ({log: myLog})); 79 | return false; 80 | } 81 | myLog.push(" Select all from chores \n"); 82 | 83 | 84 | // Close Connection steward 85 | await sqlite.closeConnection("steward"); 86 | 87 | return true; 88 | } catch (err: any) { 89 | errMess.current = `${err.message}`; 90 | setOutput(() => ({log: myLog})); 91 | return false; 92 | } 93 | } 94 | 95 | useEffect( () => { 96 | if(sqlite.isAvailable) { 97 | if (myRef.current === false) { 98 | myRef.current = true; 99 | testDatabaseIssue184().then(async res => { 100 | if(res) { 101 | myLog.push("\n* The set of tests was successful *\n"); 102 | } else { 103 | myLog.push("\n* The set of tests failed *\n"); 104 | await showAlert(errMess.current); 105 | } 106 | setOutput(() => ({log: myLog})); 107 | }); 108 | } 109 | } else { 110 | sqlite.getPlatform().then(async (ret: { platform: string; }) => { 111 | myLog.push("\n* Not available for " + 112 | ret.platform + " platform *\n"); 113 | await showAlert(errMess.current); 114 | setOutput(() => ({log: myLog})); 115 | }); 116 | } 117 | 118 | }); 119 | 120 | 121 | return ( 122 | 123 | ); 124 | }; 125 | 126 | export default TestIssue184; 127 | -------------------------------------------------------------------------------- /src/components/TestJsonImportExport.css: -------------------------------------------------------------------------------- 1 | .container-import-export { 2 | text-align: left; 3 | position: relative; 4 | left: 2px; 5 | right: 2px; 6 | top: 10px; 7 | } -------------------------------------------------------------------------------- /src/components/TestOutput.css: -------------------------------------------------------------------------------- 1 | .container-testoutput { 2 | text-align: left; 3 | position: relative; 4 | left: 2px; 5 | right: 2px; 6 | top: 10px; 7 | } -------------------------------------------------------------------------------- /src/components/TestOutput.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './TestOutput.css'; 3 | import { IonCard,IonCardContent } from '@ionic/react'; 4 | const TestOutput = (props:{dataLog: string[], errMess: string}) => { 5 | return ( 6 | 7 | 8 | 9 |
    10 | {props.dataLog.map((log: string, index: number) => ( 11 |
  • {log}
  • 12 | ))} 13 |
14 | {props.errMess.length > 0 &&

{props.errMess}

} 15 |
16 |
17 | ); 18 | 19 | }; 20 | export default TestOutput; 21 | -------------------------------------------------------------------------------- /src/components/TestUpgradeVersion.css: -------------------------------------------------------------------------------- 1 | .container-upgradeversion { 2 | text-align: left; 3 | position: relative; 4 | left: 2px; 5 | right: 2px; 6 | top: 10px; 7 | } -------------------------------------------------------------------------------- /src/components/TestUpgradeVersion.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from 'react'; 2 | import './TestUpgradeVersion.css'; 3 | import TestOutput from './TestOutput'; 4 | import { userMessages } from '../Utils/upgradeVersionUtils'; 5 | import { versionUpgrades } from '../Utils/upgrade-database-version'; 6 | 7 | import { SQLiteDBConnection, VersionUpgrade } from 'react-sqlite-hook'; 8 | 9 | import { sqlite } from '../App'; 10 | import { deleteDatabase } from '../Utils/deleteDBUtil'; 11 | import { Dialog } from '@capacitor/dialog'; 12 | 13 | const TestUpgradeVersion: React.FC = () => { 14 | const myRef = useRef(false); 15 | const myLog: string[] = []; 16 | const errMess = useRef(""); 17 | const [output, setOutput] = useState({log: myLog}); 18 | const showAlert = async (message: string) => { 19 | await Dialog.alert({ 20 | title: 'Error Dialog', 21 | message: message, 22 | }); 23 | }; 24 | const databaseUpgradeVersion = async (): Promise => { 25 | myLog.push("* Starting testDatabaseUpgradeVersion *\n"); 26 | // ************************************************ 27 | // Create Database Version 1 & Version 2 28 | // ************************************************ 29 | myLog.push("* Starting Create Version 1 *\n"); 30 | try { 31 | // initialize the connection for Database Version 1 32 | let db: SQLiteDBConnection = await sqlite 33 | .createConnection("test-updversion", false, 34 | "no-encryption", 1); 35 | // check if the databases exist 36 | // and delete it for multiple successive tests 37 | await deleteDatabase(db); 38 | 39 | // close connection to test-updversion 40 | await sqlite.closeConnection('test-updversion'); 41 | 42 | for (const upgrade of versionUpgrades) { 43 | const vUpg: VersionUpgrade = {} as VersionUpgrade; 44 | vUpg.toVersion = upgrade.toVersion; 45 | vUpg.statements = upgrade.statements; 46 | await sqlite.addUpgradeStatement( 47 | 'test-updversion', 48 | vUpg 49 | ); 50 | } 51 | db = await sqlite.createConnection("test-updversion", false, 52 | "no-encryption", 2); 53 | 54 | // open db test-updversion 55 | await db.open(); 56 | 57 | // select all users in db 58 | let ret: any = await db.query("SELECT * FROM users;"); 59 | if(ret.values.length !== 2 || ret.values[0].name !== "Whiteley" || 60 | ret.values[1].name !== "Jones") { 61 | errMess.current = `Query users not returning 2 values`; 62 | setOutput(() => ({log: myLog})); 63 | return false; 64 | } 65 | // select all user's country in db 66 | ret = await db.query("SELECT country FROM users;"); 67 | if(ret.values.length !== 2 || 68 | ret.values[0].country !== "United Kingdom" || 69 | ret.values[1].country !== "Australia") { 70 | errMess.current = `Query country not returning 2 values`; 71 | setOutput(() => ({log: myLog})); 72 | return false; 73 | } 74 | // select all messages for user 1 75 | ret = await db.query(userMessages,["1"]); 76 | if(ret.values.length !== 2 || 77 | ret.values[0].name !== "Whiteley" || 78 | ret.values[0].title !== "test message 1" || 79 | ret.values[1].name !== "Whiteley" || 80 | ret.values[1].title !== "test message 3") { 81 | errMess.current = `Query userMessages not returning 2 values`; 82 | setOutput(() => ({log: myLog})); 83 | return false; 84 | } 85 | // select all messages for user 2 86 | ret = await db.query(userMessages,["2"]); 87 | if(ret.values.length !== 1 || 88 | ret.values[0].name !== "Jones" || 89 | ret.values[0].title !== "test message 2") { 90 | errMess.current = `Query userMessages ["2"] not returning 1 values`; 91 | setOutput(() => ({log: myLog})); 92 | return false; 93 | } 94 | // close db test-updversion 95 | await db.close(); 96 | // close connection to test-updversion 97 | await sqlite.closeConnection("test-updversion"); 98 | myLog.push("* Ending Database Version 1&2 *\n"); 99 | 100 | return true; 101 | } catch (err: any) { 102 | errMess.current = `${err.message}`; 103 | setOutput(() => ({log: myLog})); 104 | return false; 105 | } 106 | } 107 | 108 | useEffect( () => { 109 | if(sqlite.isAvailable) { 110 | if (myRef.current === false) { 111 | myRef.current = true; 112 | databaseUpgradeVersion().then(async res => { 113 | if(res) { 114 | myLog.push("\n* The set of tests was successful *\n"); 115 | } else { 116 | myLog.push("\n* The set of tests failed *\n"); 117 | await showAlert(errMess.current); 118 | } 119 | setOutput(() => ({log: myLog})); 120 | }); 121 | } 122 | } else { 123 | sqlite.getPlatform().then(async (ret: { platform: string; }) => { 124 | myLog.push("\n* Not available for " + 125 | ret.platform + " platform *\n"); 126 | await showAlert(errMess.current); 127 | setOutput(() => ({log: myLog})); 128 | }); 129 | } 130 | 131 | }); 132 | 133 | return ( 134 | 135 | ); 136 | }; 137 | 138 | export default TestUpgradeVersion; 139 | -------------------------------------------------------------------------------- /src/data/messages.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "fromName": "Matt Chorsey", 4 | "subject": "New event: Trip to Vegas", 5 | "date": "9:32 AM", 6 | "id": 0 7 | }, 8 | { 9 | "fromName": "Lauren Ruthford", 10 | "subject": "Long time no chat", 11 | "date": "6:12 AM", 12 | "id": 1 13 | }, 14 | { 15 | "fromName": "Jordan Firth", 16 | "subject": "Report Results", 17 | "date": "4:55 AM", 18 | "id": 2 19 | 20 | }, 21 | { 22 | "fromName": "Bill Thomas", 23 | "subject": "The situation", 24 | "date": "Yesterday", 25 | "id": 3 26 | }, 27 | { 28 | "fromName": "Joanne Pollan", 29 | "subject": "Updated invitation: Swim lessons", 30 | "date": "Yesterday", 31 | "id": 4 32 | }, 33 | { 34 | "fromName": "Andrea Cornerston", 35 | "subject": "Last minute ask", 36 | "date": "Yesterday", 37 | "id": 5 38 | }, 39 | { 40 | "fromName": "Moe Chamont", 41 | "subject": "Family Calendar - Version 1", 42 | "date": "Last Week", 43 | "id": 6 44 | }, 45 | { 46 | "fromName": "Kelly Richardson", 47 | "subject": "Placeholder Headhots", 48 | "date": "Last Week", 49 | "id": 7 50 | } 51 | ] 52 | -------------------------------------------------------------------------------- /src/data/messages.ts: -------------------------------------------------------------------------------- 1 | import MessagesJson from './messages.json'; 2 | 3 | 4 | export interface Message { 5 | fromName: string; 6 | subject: string; 7 | date: string; 8 | id: number; 9 | } 10 | 11 | export const messagesImport: any = { 12 | database : "db-messages", 13 | version : 1, 14 | encrypted : false, 15 | mode : "full", 16 | tables :[ 17 | { 18 | name: "messages", 19 | schema: [ 20 | {column:"id", value: "INTEGER PRIMARY KEY NOT NULL"}, 21 | {column:"fromName", value:"TEXT NOT NULL"}, 22 | {column:"subject", value:"TEXT"}, 23 | {column:"date", value:"TEXT"}, 24 | {column:'sql_deleted', value:'BOOLEAN DEFAULT 0 CHECK (sql_deleted IN (0, 1))'}, 25 | {column:'last_modified', value:'INTEGER DEFAULT (strftime(\'%s\', \'now\'))'} 26 | ], 27 | indexes: [ 28 | {name: "index_user_on_fromName",value: "fromName"}, 29 | {name: "index_user_on_last_modified",value: "last_modified DESC"} 30 | ] 31 | }, 32 | 33 | ] 34 | }; 35 | 36 | export const fetchMessages = () => { 37 | const messages: Message[] = MessagesJson; 38 | return messages; 39 | } 40 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import App from './App'; 4 | import * as serviceWorkerRegistration from './serviceWorkerRegistration'; 5 | import reportWebVitals from './reportWebVitals'; 6 | import { defineCustomElements as jeepSqlite, applyPolyfills, JSX as LocalJSX } from "jeep-sqlite/loader"; 7 | import { HTMLAttributes } from 'react'; 8 | import { Capacitor } from '@capacitor/core'; 9 | import { CapacitorSQLite, SQLiteConnection, SQLiteDBConnection } from '@capacitor-community/sqlite'; 10 | 11 | type StencilToReact = { 12 | [P in keyof T]?: T[P] & Omit, 'className'> & { 13 | class?: string; 14 | }; 15 | } ; 16 | 17 | declare global { 18 | export namespace JSX { 19 | interface IntrinsicElements extends StencilToReact { 20 | } 21 | } 22 | } 23 | 24 | applyPolyfills().then(() => { 25 | jeepSqlite(window); 26 | }); 27 | window.addEventListener('DOMContentLoaded', async () => { 28 | console.log('$$$ in index $$$'); 29 | const platform = Capacitor.getPlatform(); 30 | const sqlite: SQLiteConnection = new SQLiteConnection(CapacitorSQLite) 31 | try { 32 | if(platform === "web") { 33 | const jeepEl = document.createElement("jeep-sqlite"); 34 | document.body.appendChild(jeepEl); 35 | await customElements.whenDefined('jeep-sqlite'); 36 | await sqlite.initWebStore(); 37 | } 38 | const ret = await sqlite.checkConnectionsConsistency(); 39 | const isConn = (await sqlite.isConnection("db_issue9", false)).result; 40 | var db: SQLiteDBConnection 41 | if (ret.result && isConn) { 42 | db = await sqlite.retrieveConnection("db_issue9", false); 43 | } else { 44 | db = await sqlite.createConnection("db_issue9", false, "no-encryption", 1, false); 45 | } 46 | 47 | await db.open(); 48 | let query = ` 49 | CREATE TABLE IF NOT EXISTS test ( 50 | id INTEGER PRIMARY KEY NOT NULL, 51 | name TEXT NOT NULL 52 | ); 53 | ` 54 | 55 | const res: any = await db.execute(query); 56 | console.log(`res: ${JSON.stringify(res)}`); 57 | await db.close(); 58 | await sqlite.closeConnection("db_issue9", false); 59 | const container = document.getElementById('root'); 60 | const root = createRoot(container!); 61 | root.render( 62 | 63 | 64 | 65 | ); 66 | 67 | // If you want your app to work offline and load faster, you can change 68 | // unregister() to register() below. Note this comes with some pitfalls. 69 | // Learn more about service workers: https://cra.link/PWA 70 | serviceWorkerRegistration.unregister(); 71 | 72 | // If you want to start measuring performance in your app, pass a function 73 | // to log results (for example: reportWebVitals(console.log)) 74 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 75 | reportWebVitals(); 76 | } catch (err) { 77 | console.log(`Error: ${err}`); 78 | throw new Error(`Error: ${err}`) 79 | } 80 | }); 81 | -------------------------------------------------------------------------------- /src/pages/Tab1.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/src/pages/Tab1.css -------------------------------------------------------------------------------- /src/pages/Tab1.tsx: -------------------------------------------------------------------------------- 1 | import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonList, useIonViewWillEnter } from '@ionic/react'; 2 | //import ExploreContainer from '../components/ExploreContainer'; 3 | import MessageListItem from '../components/MessageListItem'; 4 | import { useState } from 'react'; 5 | import { Message, messagesImport, fetchMessages } from '../data/messages'; 6 | import { sqlite } from '../App'; 7 | import { SQLiteDBConnection} from 'react-sqlite-hook'; 8 | import './Tab1.css'; 9 | 10 | const Tab1: React.FC = () => { 11 | const [messages, setMessages] = useState([]); 12 | 13 | useIonViewWillEnter(async () => { 14 | console.log(`in useIonViewWillEnter`) 15 | 16 | try { 17 | // check if database "db-messages" exists 18 | const isDB = (await sqlite.isDatabase('db-messages')).result; 19 | console.log(`$$$ isDB : ${isDB}`) 20 | if (!isDB) { 21 | // get messagesImport 22 | const jsonImport = messagesImport; 23 | console.log(`jsonImport: ${JSON.stringify(jsonImport)}`) 24 | // simulate a fetch to get messages 25 | const response = fetchMessages(); 26 | console.log(`response: ${JSON.stringify(response)}`) 27 | // load the messages in jsonImport 28 | const mValues = []; 29 | for (const message of response) { 30 | console.log(`message: ${JSON.stringify(message)}`) 31 | const row = []; 32 | const timestamp = Math.floor(Date.now() / 1000); 33 | row.push(message.id); 34 | row.push(message.fromName); 35 | row.push(message.subject); 36 | row.push(message.date); 37 | row.push(0); 38 | row.push(timestamp); 39 | console.log(`row: ${JSON.stringify(row)}`) 40 | mValues.push(row); 41 | } 42 | jsonImport.tables[0].values = mValues; 43 | console.log(`jsonImport: ${JSON.stringify(jsonImport)}`) 44 | const isJsonValid = await sqlite.isJsonValid(JSON.stringify(jsonImport)); 45 | console.log(`>> jsonImport: ${JSON.stringify(jsonImport)}`) 46 | if(!isJsonValid.result) { 47 | throw new Error(`Error: jsonImport Object not valid`); 48 | } 49 | const retImport: any = await sqlite.importFromJson(JSON.stringify(jsonImport)); 50 | console.log(`full import result ${retImport.changes.changes}`); 51 | if(retImport.changes.changes === -1 ) { 52 | throw new Error(`Error: importFromJson failed`); 53 | } 54 | } 55 | // create a connection for "db-messages" 56 | const db: SQLiteDBConnection = await sqlite.createConnection("db-messages"); 57 | await db.open(); 58 | // query the messages 59 | const cmd = `--test comments 60 | SELECT * 61 | /* 62 | * Author: jeepq 63 | * Purpose: To show a comment that spans multiple lines in your SQL 64 | * statement in SQLite. 65 | */ 66 | FROM messages; 67 | `; 68 | console.log(`&&&& cmd: ${cmd}`); 69 | const qValues = await db.query(cmd); 70 | const msgs = qValues.values 71 | if(msgs != null) { 72 | setMessages(msgs); 73 | } 74 | // Close Connection db-messages 75 | await sqlite.closeConnection("db-messages"); 76 | 77 | } catch (err) { 78 | console.log(`Error: ${err}`); 79 | } 80 | }); 81 | 82 | return ( 83 | 84 | 85 | 86 | Tab 1 87 | 88 | 89 | 90 | 91 | 92 | Tab 1 93 | 94 | 95 | 96 | {messages.map(m => )} 97 | 98 | 99 | 100 | ); 101 | }; 102 | 103 | export default Tab1; 104 | -------------------------------------------------------------------------------- /src/pages/Tab2.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/src/pages/Tab2.css -------------------------------------------------------------------------------- /src/pages/Tab2.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonList, IonItem, IonButton } from '@ionic/react'; 3 | import './Tab2.css'; 4 | import { sqlite } from '../App'; 5 | import { existingConn } from '../App'; 6 | 7 | const Tab2: React.FC = () => { 8 | const [isNative, setIsNative] = useState(false); 9 | sqlite.getPlatform().then((platform: any) => { 10 | if(platform.platform === "ios" || platform.platform === "android") { 11 | setIsNative(true); 12 | } 13 | }); 14 | 15 | return ( 16 | 17 | 18 | 19 | Tab 2 20 | 21 | 22 | 23 | 24 | 25 | Tab 2 26 | 27 | 28 | 29 | 30 | SQLite Two DBs Tests 31 | 32 | {existingConn.existConn && 33 | 34 | SQLite Existing Test 35 | 36 | } 37 | 38 | SQLite No Encryption Test 39 | 40 | {isNative && 41 | 42 | SQLite Encryption Test 43 | 44 | } 45 | 46 | SQLite Upgrade Version Test 47 | 48 | 49 | SQLite Json Import Export Test 50 | 51 | 52 | SQLite Copy From Assets Test 53 | 54 | 55 | Test Issue184 56 | 57 | {isNative && 58 | 59 | Test Migrate DB 60 | 61 | } 62 | {isNative && 63 | 64 | Test NC Database 65 | 66 | } 67 | 68 | 69 | 70 | ); 71 | }; 72 | 73 | export default Tab2; 74 | 75 | /* 76 | 77 | */ -------------------------------------------------------------------------------- /src/pages/Tab3.css: -------------------------------------------------------------------------------- 1 | .container-tab3 { 2 | text-align: center; 3 | position: relative; 4 | left: 2px; 5 | right: 2px; 6 | top: 10px; 7 | } -------------------------------------------------------------------------------- /src/pages/Tab3.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState, useRef } from 'react'; 2 | import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonText, useIonViewWillEnter, useIonViewWillLeave } from '@ionic/react'; 3 | import './Tab3.css'; 4 | import { SQLiteDBConnection } from 'react-sqlite-hook'; 5 | import { sqlite } from '../App'; 6 | 7 | const Tab3: React.FC = () => { 8 | const ref = useRef(false); 9 | const [tests, setTests] = useState([]); 10 | const initialize = async (): Promise => { 11 | console.log('Entering initialize'); 12 | try { 13 | let db: SQLiteDBConnection = await sqlite.createConnection("db_issue9"); 14 | await db.open(); 15 | let randomText = (Math.random() + 1).toString(36).substring(7); 16 | console.log(`Inserting ${randomText}`); 17 | await db.run("INSERT INTO test (name) VALUES (?)", [randomText]); 18 | let res: any = await db.query("SELECT * FROM test"); 19 | setTests(res.values); 20 | await db.close(); 21 | sqlite.closeConnection("db_issue9"); 22 | return ; 23 | } 24 | catch (err) { 25 | console.log(`Error: ${err}`); 26 | return ; 27 | } 28 | } 29 | useIonViewWillEnter(() => { 30 | if(ref.current === false) { 31 | initialize(); 32 | ref.current = true; 33 | } 34 | }); 35 | 36 | useIonViewWillLeave(() => { 37 | ref.current = false; 38 | }); 39 | return ( 40 | 41 | 42 | 43 | Tab 3 44 | 45 | 46 | 47 | 48 | 49 | Tab 3 50 | 51 | 52 |
53 | {tests.map((x: any, index: any) => 54 |
55 | {index} {x.name} 56 | 57 |
58 | )} 59 |
60 |
61 |
62 | ); 63 | }; 64 | 65 | export default Tab3; 66 | -------------------------------------------------------------------------------- /src/pages/ViewMessage.css: -------------------------------------------------------------------------------- 1 | #view-message-page ion-item { 2 | --inner-padding-end: 0; 3 | --background: transparent; 4 | } 5 | 6 | #view-message-page ion-label { 7 | margin-top: 12px; 8 | margin-bottom: 12px; 9 | } 10 | 11 | #view-message-page ion-item h2 { 12 | font-weight: 600; 13 | } 14 | 15 | #view-message-page ion-item .date { 16 | float: right; 17 | align-items: center; 18 | display: flex; 19 | } 20 | 21 | #view-message-page ion-item ion-icon { 22 | font-size: 42px; 23 | margin-right: 8px; 24 | } 25 | 26 | #view-message-page ion-item ion-note { 27 | font-size: 15px; 28 | margin-right: 12px; 29 | font-weight: normal; 30 | } 31 | 32 | #view-message-page h1 { 33 | margin: 0; 34 | font-weight: bold; 35 | font-size: 22px; 36 | } 37 | 38 | #view-message-page p { 39 | line-height: 22px; 40 | } -------------------------------------------------------------------------------- /src/pages/ViewMessage.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { 3 | IonBackButton, 4 | IonButtons, 5 | IonContent, 6 | IonHeader, 7 | IonIcon, 8 | IonItem, 9 | IonLabel, 10 | IonNote, 11 | IonPage, 12 | IonToolbar, 13 | useIonViewWillEnter, 14 | } from '@ionic/react'; 15 | import { personCircle } from 'ionicons/icons'; 16 | import { useParams } from 'react-router'; 17 | import { Message } from '../data/messages'; 18 | import { sqlite } from '../App'; 19 | import { SQLiteDBConnection} from 'react-sqlite-hook'; 20 | import './ViewMessage.css'; 21 | 22 | function ViewMessage() { 23 | const [message, setMessage] = useState(); 24 | const params = useParams<{ id: string }>(); 25 | 26 | useIonViewWillEnter(async () => { 27 | try { 28 | const msg = await getMessage(parseInt(params.id, 10)); 29 | setMessage(msg); 30 | } catch(err) { 31 | console.log(`Error: ${err}`); 32 | } 33 | }); 34 | 35 | const getMessage = async (id: number): Promise => { 36 | let message: Message = {} as Message; 37 | let isConn = await sqlite.isConnection("db-messages"); 38 | let db: SQLiteDBConnection; 39 | try { 40 | if(!isConn.result) { 41 | db = await sqlite.createConnection("db-messages"); 42 | } else { 43 | db = await sqlite.retrieveConnection("db-messages"); 44 | } 45 | await db.open(); 46 | // query the messages 47 | const stmt = `SELECT * FROM messages where id=${id}`; 48 | const qValues = await db.query(stmt); 49 | if (qValues && qValues.values && qValues.values.length === 1) { 50 | message = qValues.values[0]; 51 | } 52 | return Promise.resolve(message); 53 | } catch (err) { 54 | return Promise.reject(`Error: ${err}`); 55 | } 56 | 57 | } 58 | return ( 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | {message ? ( 70 | <> 71 | 72 | 73 | 74 |

75 | {message.fromName} 76 | 77 | {message.date} 78 | 79 |

80 |

81 | To: Me 82 |

83 |
84 |
85 | 86 |
87 |

{message.subject}

88 |

89 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do 90 | eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut 91 | enim ad minim veniam, quis nostrud exercitation ullamco laboris 92 | nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor 93 | in reprehenderit in voluptate velit esse cillum dolore eu fugiat 94 | nulla pariatur. Excepteur sint occaecat cupidatat non proident, 95 | sunt in culpa qui officia deserunt mollit anim id est laborum. 96 |

97 |
98 | 99 | ) : ( 100 |
Message not found
101 | )} 102 |
103 |
104 | ); 105 | } 106 | 107 | export default ViewMessage; 108 | -------------------------------------------------------------------------------- /src/pages/ViewTest.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jepiqueau/react-sqlite-app-starter/02809174e3b5bb355e506ed3bf20e1a92c74f860/src/pages/ViewTest.css -------------------------------------------------------------------------------- /src/pages/ViewTest.tsx: -------------------------------------------------------------------------------- 1 | import { useParams } from 'react-router'; 2 | import './ViewTest.css'; 3 | import { 4 | IonBackButton, 5 | IonButtons, 6 | IonContent, 7 | IonHeader, 8 | IonPage, 9 | IonToolbar, 10 | } from '@ionic/react'; 11 | import Test2dbs from '../components/Test2dbs'; 12 | import ExistingConnection from '../components/ExistingConnection'; 13 | import NoEncryption from '../components/NoEncryption'; 14 | import TestEncryption from '../components/TestEncryption'; 15 | import TestJsonImportExport from '../components/TestJsonImportExport'; 16 | import CopyFromAssets from '../components/CopyFromAssets'; 17 | import TestUpgradeVersion from '../components/TestUpgradeVersion'; 18 | import TestIssue184 from '../components/TestIssue184'; 19 | import MigrateDB from '../components/MigrateDB'; 20 | import NonConformedDB from '../components/NonConformedDB'; 21 | 22 | 23 | const ViewTest: React.FC = () => { 24 | 25 | const params = useParams<{ name: string }>(); 26 | 27 | const setTestName = (testName:string) => { 28 | switch (testName) { 29 | case 'NoEncryption': 30 | return () 31 | case 'Test2dbs': 32 | return () 33 | case 'ExistingConnection': 34 | return () 35 | case 'TestEncryption': 36 | return () 37 | case 'TestJsonImportExport': 38 | return () 39 | case 'CopyFromAssets': 40 | return () 41 | case 'TestUpgradeVersion': 42 | return () 43 | case 'TestIssue184': 44 | return () 45 | case 'MigrateDB': 46 | return () 47 | case 'NonConformedDB': 48 | return () 49 | default: 50 | console.log(`Test name: ${testName} does not exist`); 51 | } 52 | } 53 | return ( 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | {setTestName(params.name)} 65 | 66 | 67 | ) 68 | }; 69 | export default ViewTest; 70 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /src/service-worker.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /* eslint-disable no-restricted-globals */ 3 | 4 | // This service worker can be customized! 5 | // See https://developers.google.com/web/tools/workbox/modules 6 | // for the list of available Workbox modules, or add any other 7 | // code you'd like. 8 | // You can also remove this file if you'd prefer not to use a 9 | // service worker, and the Workbox build step will be skipped. 10 | 11 | import { clientsClaim } from 'workbox-core'; 12 | import { ExpirationPlugin } from 'workbox-expiration'; 13 | import { precacheAndRoute, createHandlerBoundToURL } from 'workbox-precaching'; 14 | import { registerRoute } from 'workbox-routing'; 15 | import { StaleWhileRevalidate } from 'workbox-strategies'; 16 | 17 | declare const self: ServiceWorkerGlobalScope; 18 | 19 | clientsClaim(); 20 | 21 | // Precache all of the assets generated by your build process. 22 | // Their URLs are injected into the manifest variable below. 23 | // This variable must be present somewhere in your service worker file, 24 | // even if you decide not to use precaching. See https://cra.link/PWA 25 | precacheAndRoute(self.__WB_MANIFEST); 26 | 27 | // Set up App Shell-style routing, so that all navigation requests 28 | // are fulfilled with your index.html shell. Learn more at 29 | // https://developers.google.com/web/fundamentals/architecture/app-shell 30 | const fileExtensionRegexp = new RegExp('/[^/?]+\\.[^/]+$'); 31 | registerRoute( 32 | // Return false to exempt requests from being fulfilled by index.html. 33 | ({ request, url }: { request: Request; url: URL }) => { 34 | // If this isn't a navigation, skip. 35 | if (request.mode !== 'navigate') { 36 | return false; 37 | } 38 | 39 | // If this is a URL that starts with /_, skip. 40 | if (url.pathname.startsWith('/_')) { 41 | return false; 42 | } 43 | 44 | // If this looks like a URL for a resource, because it contains 45 | // a file extension, skip. 46 | if (url.pathname.match(fileExtensionRegexp)) { 47 | return false; 48 | } 49 | 50 | // Return true to signal that we want to use the handler. 51 | return true; 52 | }, 53 | createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html') 54 | ); 55 | 56 | // An example runtime caching route for requests that aren't handled by the 57 | // precache, in this case same-origin .png requests like those from in public/ 58 | registerRoute( 59 | // Add in any other file extensions or routing criteria as needed. 60 | ({ url }) => url.origin === self.location.origin && url.pathname.endsWith('.png'), 61 | // Customize this strategy as needed, e.g., by changing to CacheFirst. 62 | new StaleWhileRevalidate({ 63 | cacheName: 'images', 64 | plugins: [ 65 | // Ensure that once this runtime cache reaches a maximum size the 66 | // least-recently used images are removed. 67 | new ExpirationPlugin({ maxEntries: 50 }), 68 | ], 69 | }) 70 | ); 71 | 72 | // This allows the web app to trigger skipWaiting via 73 | // registration.waiting.postMessage({type: 'SKIP_WAITING'}) 74 | self.addEventListener('message', (event) => { 75 | if (event.data && event.data.type === 'SKIP_WAITING') { 76 | self.skipWaiting(); 77 | } 78 | }); 79 | 80 | // Any other custom service worker logic can go here. 81 | -------------------------------------------------------------------------------- /src/serviceWorkerRegistration.ts: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://cra.link/PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.0/8 are considered localhost for IPv4. 18 | window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/) 19 | ); 20 | 21 | type Config = { 22 | onSuccess?: (registration: ServiceWorkerRegistration) => void; 23 | onUpdate?: (registration: ServiceWorkerRegistration) => void; 24 | }; 25 | 26 | export function register(config?: Config) { 27 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 28 | // The URL constructor is available in all browsers that support SW. 29 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 30 | if (publicUrl.origin !== window.location.origin) { 31 | // Our service worker won't work if PUBLIC_URL is on a different origin 32 | // from what our page is served on. This might happen if a CDN is used to 33 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 34 | return; 35 | } 36 | 37 | window.addEventListener('load', () => { 38 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 39 | 40 | if (isLocalhost) { 41 | // This is running on localhost. Let's check if a service worker still exists or not. 42 | checkValidServiceWorker(swUrl, config); 43 | 44 | // Add some additional logging to localhost, pointing developers to the 45 | // service worker/PWA documentation. 46 | navigator.serviceWorker.ready.then(() => { 47 | console.log( 48 | 'This web app is being served cache-first by a service ' + 49 | 'worker. To learn more, visit https://cra.link/PWA' 50 | ); 51 | }); 52 | } else { 53 | // Is not localhost. Just register service worker 54 | registerValidSW(swUrl, config); 55 | } 56 | }); 57 | } 58 | } 59 | 60 | function registerValidSW(swUrl: string, config?: Config) { 61 | navigator.serviceWorker 62 | .register(swUrl) 63 | .then((registration) => { 64 | registration.onupdatefound = () => { 65 | const installingWorker = registration.installing; 66 | if (installingWorker == null) { 67 | return; 68 | } 69 | installingWorker.onstatechange = () => { 70 | if (installingWorker.state === 'installed') { 71 | if (navigator.serviceWorker.controller) { 72 | // At this point, the updated precached content has been fetched, 73 | // but the previous service worker will still serve the older 74 | // content until all client tabs are closed. 75 | console.log( 76 | 'New content is available and will be used when all ' + 77 | 'tabs for this page are closed. See https://cra.link/PWA.' 78 | ); 79 | 80 | // Execute callback 81 | if (config && config.onUpdate) { 82 | config.onUpdate(registration); 83 | } 84 | } else { 85 | // At this point, everything has been precached. 86 | // It's the perfect time to display a 87 | // "Content is cached for offline use." message. 88 | console.log('Content is cached for offline use.'); 89 | 90 | // Execute callback 91 | if (config && config.onSuccess) { 92 | config.onSuccess(registration); 93 | } 94 | } 95 | } 96 | }; 97 | }; 98 | }) 99 | .catch((error) => { 100 | console.error('Error during service worker registration:', error); 101 | }); 102 | } 103 | 104 | function checkValidServiceWorker(swUrl: string, config?: Config) { 105 | // Check if the service worker can be found. If it can't reload the page. 106 | fetch(swUrl, { 107 | headers: { 'Service-Worker': 'script' }, 108 | }) 109 | .then((response) => { 110 | // Ensure service worker exists, and that we really are getting a JS file. 111 | const contentType = response.headers.get('content-type'); 112 | if ( 113 | response.status === 404 || 114 | (contentType != null && contentType.indexOf('javascript') === -1) 115 | ) { 116 | // No service worker found. Probably a different app. Reload the page. 117 | navigator.serviceWorker.ready.then((registration) => { 118 | registration.unregister().then(() => { 119 | window.location.reload(); 120 | }); 121 | }); 122 | } else { 123 | // Service worker found. Proceed as normal. 124 | registerValidSW(swUrl, config); 125 | } 126 | }) 127 | .catch(() => { 128 | console.log('No internet connection found. App is running in offline mode.'); 129 | }); 130 | } 131 | 132 | export function unregister() { 133 | if ('serviceWorker' in navigator) { 134 | navigator.serviceWorker.ready 135 | .then((registration) => { 136 | registration.unregister(); 137 | }) 138 | .catch((error) => { 139 | console.error(error.message); 140 | }); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/services/DarkModeService.tsx: -------------------------------------------------------------------------------- 1 | 2 | class DarkModeService { 3 | 4 | /** 5 | * Set the Dark/Light Mode 6 | * @param shouldEnable boolean 7 | */ 8 | enableDarkTheme(shouldEnable:boolean) { 9 | document.body.classList.toggle("dark",shouldEnable); 10 | } 11 | 12 | } 13 | export {DarkModeService}; -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | 7 | // Mock matchmedia 8 | window.matchMedia = window.matchMedia || function() { 9 | return { 10 | matches: false, 11 | addListener: function() {}, 12 | removeListener: function() {} 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /src/theme/variables.css: -------------------------------------------------------------------------------- 1 | /* Ionic Variables and Theming. For more info, please see: 2 | http://ionicframework.com/docs/theming/ */ 3 | 4 | /** Ionic CSS Variables **/ 5 | :root { 6 | /** primary **/ 7 | --ion-color-primary: #3880ff; 8 | --ion-color-primary-rgb: 56, 128, 255; 9 | --ion-color-primary-contrast: #ffffff; 10 | --ion-color-primary-contrast-rgb: 255, 255, 255; 11 | --ion-color-primary-shade: #3171e0; 12 | --ion-color-primary-tint: #4c8dff; 13 | 14 | /** secondary **/ 15 | --ion-color-secondary: #3dc2ff; 16 | --ion-color-secondary-rgb: 61, 194, 255; 17 | --ion-color-secondary-contrast: #ffffff; 18 | --ion-color-secondary-contrast-rgb: 255, 255, 255; 19 | --ion-color-secondary-shade: #36abe0; 20 | --ion-color-secondary-tint: #50c8ff; 21 | 22 | /** tertiary **/ 23 | --ion-color-tertiary: #5260ff; 24 | --ion-color-tertiary-rgb: 82, 96, 255; 25 | --ion-color-tertiary-contrast: #ffffff; 26 | --ion-color-tertiary-contrast-rgb: 255, 255, 255; 27 | --ion-color-tertiary-shade: #4854e0; 28 | --ion-color-tertiary-tint: #6370ff; 29 | 30 | /** success **/ 31 | --ion-color-success: #2dd36f; 32 | --ion-color-success-rgb: 45, 211, 111; 33 | --ion-color-success-contrast: #ffffff; 34 | --ion-color-success-contrast-rgb: 255, 255, 255; 35 | --ion-color-success-shade: #28ba62; 36 | --ion-color-success-tint: #42d77d; 37 | 38 | /** warning **/ 39 | --ion-color-warning: #ffc409; 40 | --ion-color-warning-rgb: 255, 196, 9; 41 | --ion-color-warning-contrast: #000000; 42 | --ion-color-warning-contrast-rgb: 0, 0, 0; 43 | --ion-color-warning-shade: #e0ac08; 44 | --ion-color-warning-tint: #ffca22; 45 | 46 | /** danger **/ 47 | --ion-color-danger: #eb445a; 48 | --ion-color-danger-rgb: 235, 68, 90; 49 | --ion-color-danger-contrast: #ffffff; 50 | --ion-color-danger-contrast-rgb: 255, 255, 255; 51 | --ion-color-danger-shade: #cf3c4f; 52 | --ion-color-danger-tint: #ed576b; 53 | 54 | /** dark **/ 55 | --ion-color-dark: #222428; 56 | --ion-color-dark-rgb: 34, 36, 40; 57 | --ion-color-dark-contrast: #ffffff; 58 | --ion-color-dark-contrast-rgb: 255, 255, 255; 59 | --ion-color-dark-shade: #1e2023; 60 | --ion-color-dark-tint: #383a3e; 61 | 62 | /** medium **/ 63 | --ion-color-medium: #92949c; 64 | --ion-color-medium-rgb: 146, 148, 156; 65 | --ion-color-medium-contrast: #ffffff; 66 | --ion-color-medium-contrast-rgb: 255, 255, 255; 67 | --ion-color-medium-shade: #808289; 68 | --ion-color-medium-tint: #9d9fa6; 69 | 70 | /** light **/ 71 | --ion-color-light: #f4f5f8; 72 | --ion-color-light-rgb: 244, 245, 248; 73 | --ion-color-light-contrast: #000000; 74 | --ion-color-light-contrast-rgb: 0, 0, 0; 75 | --ion-color-light-shade: #d7d8da; 76 | --ion-color-light-tint: #f5f6f9; 77 | } 78 | 79 | @media (prefers-color-scheme: dark) { 80 | /* 81 | * Dark Colors 82 | * ------------------------------------------- 83 | */ 84 | 85 | body { 86 | --ion-color-primary: #428cff; 87 | --ion-color-primary-rgb: 66,140,255; 88 | --ion-color-primary-contrast: #ffffff; 89 | --ion-color-primary-contrast-rgb: 255,255,255; 90 | --ion-color-primary-shade: #3a7be0; 91 | --ion-color-primary-tint: #5598ff; 92 | 93 | --ion-color-secondary: #50c8ff; 94 | --ion-color-secondary-rgb: 80,200,255; 95 | --ion-color-secondary-contrast: #ffffff; 96 | --ion-color-secondary-contrast-rgb: 255,255,255; 97 | --ion-color-secondary-shade: #46b0e0; 98 | --ion-color-secondary-tint: #62ceff; 99 | 100 | --ion-color-tertiary: #6a64ff; 101 | --ion-color-tertiary-rgb: 106,100,255; 102 | --ion-color-tertiary-contrast: #ffffff; 103 | --ion-color-tertiary-contrast-rgb: 255,255,255; 104 | --ion-color-tertiary-shade: #5d58e0; 105 | --ion-color-tertiary-tint: #7974ff; 106 | 107 | --ion-color-success: #2fdf75; 108 | --ion-color-success-rgb: 47,223,117; 109 | --ion-color-success-contrast: #000000; 110 | --ion-color-success-contrast-rgb: 0,0,0; 111 | --ion-color-success-shade: #29c467; 112 | --ion-color-success-tint: #44e283; 113 | 114 | --ion-color-warning: #ffd534; 115 | --ion-color-warning-rgb: 255,213,52; 116 | --ion-color-warning-contrast: #000000; 117 | --ion-color-warning-contrast-rgb: 0,0,0; 118 | --ion-color-warning-shade: #e0bb2e; 119 | --ion-color-warning-tint: #ffd948; 120 | 121 | --ion-color-danger: #ff4961; 122 | --ion-color-danger-rgb: 255,73,97; 123 | --ion-color-danger-contrast: #ffffff; 124 | --ion-color-danger-contrast-rgb: 255,255,255; 125 | --ion-color-danger-shade: #e04055; 126 | --ion-color-danger-tint: #ff5b71; 127 | 128 | --ion-color-dark: #f4f5f8; 129 | --ion-color-dark-rgb: 244,245,248; 130 | --ion-color-dark-contrast: #000000; 131 | --ion-color-dark-contrast-rgb: 0,0,0; 132 | --ion-color-dark-shade: #d7d8da; 133 | --ion-color-dark-tint: #f5f6f9; 134 | 135 | --ion-color-medium: #989aa2; 136 | --ion-color-medium-rgb: 152,154,162; 137 | --ion-color-medium-contrast: #000000; 138 | --ion-color-medium-contrast-rgb: 0,0,0; 139 | --ion-color-medium-shade: #86888f; 140 | --ion-color-medium-tint: #a2a4ab; 141 | 142 | --ion-color-light: #222428; 143 | --ion-color-light-rgb: 34,36,40; 144 | --ion-color-light-contrast: #ffffff; 145 | --ion-color-light-contrast-rgb: 255,255,255; 146 | --ion-color-light-shade: #1e2023; 147 | --ion-color-light-tint: #383a3e; 148 | } 149 | 150 | /* 151 | * iOS Dark Theme 152 | * ------------------------------------------- 153 | */ 154 | 155 | .ios body { 156 | --ion-background-color: #000000; 157 | --ion-background-color-rgb: 0,0,0; 158 | 159 | --ion-text-color: #ffffff; 160 | --ion-text-color-rgb: 255,255,255; 161 | 162 | --ion-color-step-50: #0d0d0d; 163 | --ion-color-step-100: #1a1a1a; 164 | --ion-color-step-150: #262626; 165 | --ion-color-step-200: #333333; 166 | --ion-color-step-250: #404040; 167 | --ion-color-step-300: #4d4d4d; 168 | --ion-color-step-350: #595959; 169 | --ion-color-step-400: #666666; 170 | --ion-color-step-450: #737373; 171 | --ion-color-step-500: #808080; 172 | --ion-color-step-550: #8c8c8c; 173 | --ion-color-step-600: #999999; 174 | --ion-color-step-650: #a6a6a6; 175 | --ion-color-step-700: #b3b3b3; 176 | --ion-color-step-750: #bfbfbf; 177 | --ion-color-step-800: #cccccc; 178 | --ion-color-step-850: #d9d9d9; 179 | --ion-color-step-900: #e6e6e6; 180 | --ion-color-step-950: #f2f2f2; 181 | 182 | --ion-item-background: #000000; 183 | 184 | --ion-card-background: #1c1c1d; 185 | } 186 | 187 | .ios ion-modal { 188 | --ion-background-color: var(--ion-color-step-100); 189 | --ion-toolbar-background: var(--ion-color-step-150); 190 | --ion-toolbar-border-color: var(--ion-color-step-250); 191 | } 192 | 193 | 194 | /* 195 | * Material Design Dark Theme 196 | * ------------------------------------------- 197 | */ 198 | 199 | .md body { 200 | --ion-background-color: #121212; 201 | --ion-background-color-rgb: 18,18,18; 202 | 203 | --ion-text-color: #ffffff; 204 | --ion-text-color-rgb: 255,255,255; 205 | 206 | --ion-border-color: #222222; 207 | 208 | --ion-color-step-50: #1e1e1e; 209 | --ion-color-step-100: #2a2a2a; 210 | --ion-color-step-150: #363636; 211 | --ion-color-step-200: #414141; 212 | --ion-color-step-250: #4d4d4d; 213 | --ion-color-step-300: #595959; 214 | --ion-color-step-350: #656565; 215 | --ion-color-step-400: #717171; 216 | --ion-color-step-450: #7d7d7d; 217 | --ion-color-step-500: #898989; 218 | --ion-color-step-550: #949494; 219 | --ion-color-step-600: #a0a0a0; 220 | --ion-color-step-650: #acacac; 221 | --ion-color-step-700: #b8b8b8; 222 | --ion-color-step-750: #c4c4c4; 223 | --ion-color-step-800: #d0d0d0; 224 | --ion-color-step-850: #dbdbdb; 225 | --ion-color-step-900: #e7e7e7; 226 | --ion-color-step-950: #f3f3f3; 227 | 228 | --ion-item-background: #1e1e1e; 229 | 230 | --ion-toolbar-background: #1f1f1f; 231 | 232 | --ion-tab-bar-background: #1f1f1f; 233 | 234 | --ion-card-background: #1e1e1e; 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "noEmit": true, 20 | "jsx": "react-jsx", 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": [ 24 | "src/**/*.ts", 25 | "src/**/*.tsx", 26 | "./capacitor.config.ts", 27 | "./capacitor.config.js" 28 | ] 29 | } 30 | --------------------------------------------------------------------------------