├── .browserslistrc ├── .editorconfig ├── .eslintrc.json ├── .firebase └── hosting.d3d3.cache ├── .firebaserc ├── .gitignore ├── README.md ├── android ├── .gitignore ├── .idea │ ├── .gitignore │ ├── compiler.xml │ ├── jarRepositories.xml │ └── misc.xml ├── app │ ├── .gitignore │ ├── build.gradle │ ├── capacitor.build.gradle │ ├── google-services.json │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── getcapacitor │ │ │ └── myapp │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ │ ├── capacitor.config.json │ │ │ └── capacitor.plugins.json │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── app │ │ │ │ └── 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 │ │ │ └── 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 ├── angular.json ├── capacitor.config.ts ├── e2e ├── protractor.conf.js ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.json ├── firebase.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 │ ├── GoogleService-Info.plist │ ├── Info.plist │ ├── capacitor.config.json │ └── config.xml │ └── Podfile ├── karma.conf.js ├── package-lock.json ├── package.json ├── src ├── app │ ├── app-routing.module.ts │ ├── app.component.html │ ├── app.component.scss │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.module.ts │ ├── private-content │ │ ├── private-content.module.ts │ │ ├── private-content.page.html │ │ ├── private-content.page.scss │ │ ├── private-content.page.spec.ts │ │ ├── private-content.page.ts │ │ └── private-content.resolver.ts │ ├── tab1 │ │ ├── tab1.module.ts │ │ ├── tab1.page.html │ │ ├── tab1.page.scss │ │ ├── tab1.page.spec.ts │ │ ├── tab1.page.ts │ │ └── tab1.resolver.ts │ ├── tab2 │ │ ├── tab2.module.ts │ │ ├── tab2.page.html │ │ ├── tab2.page.scss │ │ ├── tab2.page.spec.ts │ │ └── tab2.page.ts │ ├── tabs │ │ ├── tabs-routing.module.ts │ │ ├── tabs.module.ts │ │ ├── tabs.page.html │ │ ├── tabs.page.scss │ │ ├── tabs.page.spec.ts │ │ └── tabs.page.ts │ └── utils │ │ ├── firebase-auth-helper.ts │ │ ├── models │ │ └── image-listing.model.ts │ │ ├── services │ │ ├── data.service.spec.ts │ │ └── data.service.ts │ │ └── shell │ │ ├── aspect-ratio │ │ ├── aspect-ratio.component.html │ │ ├── aspect-ratio.component.scss │ │ └── aspect-ratio.component.ts │ │ ├── data-store.ts │ │ ├── image-shell │ │ ├── image-shell.component.html │ │ ├── image-shell.component.scss │ │ └── image-shell.component.ts │ │ └── shell.module.ts ├── assets │ ├── icon │ │ └── favicon.png │ └── shapes.svg ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── global.scss ├── index.html ├── main.ts ├── polyfills.ts ├── test.ts ├── theme │ └── variables.scss └── zone-flags.ts ├── tsconfig.app.json ├── tsconfig.json └── tsconfig.spec.json /.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # For the full list of supported browsers by the Angular framework, please see: 6 | # https://angular.io/guide/browser-support 7 | 8 | # You can see what browsers were selected by your queries by running: 9 | # npx browserslist 10 | 11 | last 1 Chrome version 12 | last 1 Firefox version 13 | last 2 Edge major versions 14 | last 2 Safari major versions 15 | last 2 iOS major versions 16 | Firefox ESR 17 | not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. 18 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs 2 | # editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 2 9 | 10 | # We recommend you to keep these unchanged 11 | end_of_line = lf 12 | charset = utf-8 13 | trim_trailing_whitespace = true 14 | insert_final_newline = true 15 | 16 | [*.md] 17 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": ["projects/**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts"], 7 | "parserOptions": { 8 | "project": ["tsconfig.json", "e2e/tsconfig.json"], 9 | "createDefaultProgram": true 10 | }, 11 | "extends": [ 12 | "plugin:@angular-eslint/ng-cli-compat", 13 | "plugin:@angular-eslint/ng-cli-compat--formatting-add-on", 14 | "plugin:@angular-eslint/template/process-inline-templates" 15 | ], 16 | "rules": { 17 | "@angular-eslint/component-class-suffix": [ 18 | "error", 19 | { 20 | "suffixes": ["Page", "Component"] 21 | } 22 | ], 23 | "@angular-eslint/component-selector": [ 24 | "error", 25 | { 26 | "type": "element", 27 | "prefix": "app", 28 | "style": "kebab-case" 29 | } 30 | ], 31 | "@angular-eslint/directive-selector": [ 32 | "error", 33 | { 34 | "type": "attribute", 35 | "prefix": "app", 36 | "style": "camelCase" 37 | } 38 | ] 39 | } 40 | }, 41 | { 42 | "files": ["*.html"], 43 | "extends": ["plugin:@angular-eslint/template/recommended"], 44 | "rules": {} 45 | } 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "targets": { 3 | "ionic-contacts-app": { 4 | "hosting": { 5 | "app": [ 6 | "storage-tutorial" 7 | ] 8 | } 9 | } 10 | }, 11 | "projects": { 12 | "storage-tutorial": "storage-tutorial" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Specifies intentionally untracked files to ignore when using Git 2 | # http://git-scm.com/docs/gitignore 3 | 4 | *~ 5 | *.sw[mnpcod] 6 | .tmp 7 | *.tmp 8 | *.tmp.* 9 | *.sublime-project 10 | *.sublime-workspace 11 | .DS_Store 12 | Thumbs.db 13 | UserInterfaceState.xcuserstate 14 | $RECYCLE.BIN/ 15 | 16 | *.log 17 | log.txt 18 | npm-debug.log* 19 | 20 | /.idea 21 | /.ionic 22 | /.sass-cache 23 | /.sourcemaps 24 | /.versions 25 | /.vscode 26 | /coverage 27 | /dist 28 | /node_modules 29 | /platforms 30 | /plugins 31 | /www 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Upload Images to Firebase Storage from your Ionic app 2 | 3 | This app shows how to use Firebase Storage from an Ionic app. 4 | 5 | In Firebase Storage files can be either public or protected using security rules to determine who has access to them. 6 | 7 | We included examples of both cases: a section with public files visible to everyone and a private section, with authentication so the files can restrict access to content by user. The app includes the code to upload, list, and delete protected files. 8 | 9 | - [Demo](https://storage-tutorial.web.app) 10 | - [Tutorial](https://ionicframework.com/blog/building-an-ionic-app-with-protected-private-content/) 11 | - [Ionic Freebies](https://ionicthemes.com/templates/category/freebies) 12 | - [Ionic 6 Starters](https://ionicthemes.com/templates/ionic5-full-starter-app) 13 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | -------------------------------------------------------------------------------- /android/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /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.ionictutorial.app" 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 project(':capacitor-android') 36 | testImplementation "junit:junit:$junitVersion" 37 | androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" 38 | androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" 39 | implementation project(':capacitor-cordova-android-plugins') 40 | } 41 | 42 | apply from: 'capacitor.build.gradle' 43 | 44 | try { 45 | def servicesJSON = file('google-services.json') 46 | if (servicesJSON.text) { 47 | apply plugin: 'com.google.gms.google-services' 48 | } 49 | } catch(Exception e) { 50 | logger.warn("google-services.json not found, google-services plugin not applied. Push Notifications won't work") 51 | } 52 | -------------------------------------------------------------------------------- /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_1_8 6 | targetCompatibility JavaVersion.VERSION_1_8 7 | } 8 | } 9 | 10 | apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" 11 | dependencies { 12 | implementation project(':capacitor-app') 13 | implementation project(':capacitor-camera') 14 | implementation project(':capacitor-filesystem') 15 | implementation project(':capacitor-haptics') 16 | implementation project(':capacitor-keyboard') 17 | implementation project(':capacitor-status-bar') 18 | implementation project(':capacitor-storage') 19 | 20 | } 21 | 22 | 23 | if (hasProperty('postBuildExtras')) { 24 | postBuildExtras() 25 | } 26 | -------------------------------------------------------------------------------- /android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "38298401556", 4 | "project_id": "ionic-contacts-app", 5 | "storage_bucket": "ionic-contacts-app.appspot.com" 6 | }, 7 | "client": [ 8 | { 9 | "client_info": { 10 | "mobilesdk_app_id": "1:38298401556:android:f620159844c1de2674a001", 11 | "android_client_info": { 12 | "package_name": "com.ionictutorial.app" 13 | } 14 | }, 15 | "oauth_client": [ 16 | { 17 | "client_id": "38298401556-msemdjedljp89nnveovvid82i8uodhjb.apps.googleusercontent.com", 18 | "client_type": 3 19 | } 20 | ], 21 | "api_key": [ 22 | { 23 | "current_key": "AIzaSyBgVVyO09fy566UpH1dynXPaUyZc4N5wKo" 24 | } 25 | ], 26 | "services": { 27 | "appinvite_service": { 28 | "other_platform_oauth_client": [ 29 | { 30 | "client_id": "38298401556-msemdjedljp89nnveovvid82i8uodhjb.apps.googleusercontent.com", 31 | "client_type": 3 32 | } 33 | ] 34 | } 35 | } 36 | } 37 | ], 38 | "configuration_version": "1" 39 | } -------------------------------------------------------------------------------- /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 | 12 | 13 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 32 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /android/app/src/main/assets/capacitor.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "appId": "io.ionic.starter", 3 | "appName": "firebase-database-tutorial", 4 | "webDir": "www", 5 | "bundledWebRuntime": false 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/assets/capacitor.plugins.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pkg": "@capacitor/app", 4 | "classpath": "com.capacitorjs.plugins.app.AppPlugin" 5 | }, 6 | { 7 | "pkg": "@capacitor/camera", 8 | "classpath": "com.capacitorjs.plugins.camera.CameraPlugin" 9 | }, 10 | { 11 | "pkg": "@capacitor/filesystem", 12 | "classpath": "com.capacitorjs.plugins.filesystem.FilesystemPlugin" 13 | }, 14 | { 15 | "pkg": "@capacitor/haptics", 16 | "classpath": "com.capacitorjs.plugins.haptics.HapticsPlugin" 17 | }, 18 | { 19 | "pkg": "@capacitor/keyboard", 20 | "classpath": "com.capacitorjs.plugins.keyboard.KeyboardPlugin" 21 | }, 22 | { 23 | "pkg": "@capacitor/status-bar", 24 | "classpath": "com.capacitorjs.plugins.statusbar.StatusBarPlugin" 25 | }, 26 | { 27 | "pkg": "@capacitor/storage", 28 | "classpath": "com.capacitorjs.plugins.storage.StoragePlugin" 29 | } 30 | ] 31 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/example/app/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.app; 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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/android/app/src/main/res/drawable-land-hdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-land-mdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/android/app/src/main/res/drawable-land-mdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-land-xhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/android/app/src/main/res/drawable-land-xhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-land-xxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/android/app/src/main/res/drawable-land-xxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-land-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/android/app/src/main/res/drawable-land-xxxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-port-hdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/android/app/src/main/res/drawable-port-hdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-port-mdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/android/app/src/main/res/drawable-port-mdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-port-xhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/android/app/src/main/res/drawable-port-xhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-port-xxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/android/app/src/main/res/drawable-port-xxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-port-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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 | firebase-file-storage-tutorial 4 | firebase-file-storage-tutorial 5 | com.example.app 6 | com.example.app 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/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 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:4.2.1' 11 | classpath 'com.google.gms:google-services:4.3.5' 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 | jcenter() 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-app' 6 | project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android') 7 | 8 | include ':capacitor-camera' 9 | project(':capacitor-camera').projectDir = new File('../node_modules/@capacitor/camera/android') 10 | 11 | include ':capacitor-filesystem' 12 | project(':capacitor-filesystem').projectDir = new File('../node_modules/@capacitor/filesystem/android') 13 | 14 | include ':capacitor-haptics' 15 | project(':capacitor-haptics').projectDir = new File('../node_modules/@capacitor/haptics/android') 16 | 17 | include ':capacitor-keyboard' 18 | project(':capacitor-keyboard').projectDir = new File('../node_modules/@capacitor/keyboard/android') 19 | 20 | include ':capacitor-status-bar' 21 | project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android') 22 | 23 | include ':capacitor-storage' 24 | project(':capacitor-storage').projectDir = new File('../node_modules/@capacitor/storage/android') 25 | -------------------------------------------------------------------------------- /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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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.0-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /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 = 21 3 | compileSdkVersion = 30 4 | targetSdkVersion = 30 5 | androidxActivityVersion = '1.2.0' 6 | androidxAppCompatVersion = '1.2.0' 7 | androidxCoordinatorLayoutVersion = '1.1.0' 8 | androidxCoreVersion = '1.3.2' 9 | androidxFragmentVersion = '1.3.0' 10 | junitVersion = '4.13.1' 11 | androidxJunitVersion = '1.1.2' 12 | androidxEspressoCoreVersion = '3.3.0' 13 | cordovaAndroidVersion = '7.0.0' 14 | } -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "defaultProject": "app", 5 | "newProjectRoot": "projects", 6 | "projects": { 7 | "app": { 8 | "root": "", 9 | "sourceRoot": "src", 10 | "projectType": "application", 11 | "prefix": "app", 12 | "schematics": {}, 13 | "architect": { 14 | "build": { 15 | "builder": "@angular-devkit/build-angular:browser", 16 | "options": { 17 | "outputPath": "www", 18 | "index": "src/index.html", 19 | "main": "src/main.ts", 20 | "polyfills": "src/polyfills.ts", 21 | "tsConfig": "tsconfig.app.json", 22 | "assets": [ 23 | { 24 | "glob": "**/*", 25 | "input": "src/assets", 26 | "output": "assets" 27 | }, 28 | { 29 | "glob": "**/*.svg", 30 | "input": "node_modules/ionicons/dist/ionicons/svg", 31 | "output": "./svg" 32 | } 33 | ], 34 | "styles": [ 35 | "src/theme/variables.scss", 36 | "src/global.scss" 37 | ], 38 | "scripts": [], 39 | "aot": false, 40 | "vendorChunk": true, 41 | "extractLicenses": false, 42 | "buildOptimizer": false, 43 | "sourceMap": true, 44 | "optimization": false, 45 | "namedChunks": true 46 | }, 47 | "configurations": { 48 | "production": { 49 | "fileReplacements": [ 50 | { 51 | "replace": "src/environments/environment.ts", 52 | "with": "src/environments/environment.prod.ts" 53 | } 54 | ], 55 | "optimization": true, 56 | "outputHashing": "all", 57 | "sourceMap": false, 58 | "namedChunks": false, 59 | "aot": true, 60 | "extractLicenses": true, 61 | "vendorChunk": false, 62 | "buildOptimizer": true, 63 | "budgets": [ 64 | { 65 | "type": "initial", 66 | "maximumWarning": "2mb", 67 | "maximumError": "5mb" 68 | } 69 | ] 70 | }, 71 | "ci": { 72 | "progress": false 73 | } 74 | } 75 | }, 76 | "serve": { 77 | "builder": "@angular-devkit/build-angular:dev-server", 78 | "options": { 79 | "browserTarget": "app:build" 80 | }, 81 | "configurations": { 82 | "production": { 83 | "browserTarget": "app:build:production" 84 | }, 85 | "ci": { 86 | "progress": false 87 | } 88 | } 89 | }, 90 | "extract-i18n": { 91 | "builder": "@angular-devkit/build-angular:extract-i18n", 92 | "options": { 93 | "browserTarget": "app:build" 94 | } 95 | }, 96 | "test": { 97 | "builder": "@angular-devkit/build-angular:karma", 98 | "options": { 99 | "main": "src/test.ts", 100 | "polyfills": "src/polyfills.ts", 101 | "tsConfig": "tsconfig.spec.json", 102 | "karmaConfig": "karma.conf.js", 103 | "styles": [], 104 | "scripts": [], 105 | "assets": [ 106 | { 107 | "glob": "favicon.ico", 108 | "input": "src/", 109 | "output": "/" 110 | }, 111 | { 112 | "glob": "**/*", 113 | "input": "src/assets", 114 | "output": "/assets" 115 | } 116 | ] 117 | }, 118 | "configurations": { 119 | "ci": { 120 | "progress": false, 121 | "watch": false 122 | } 123 | } 124 | }, 125 | "lint": { 126 | "builder": "@angular-eslint/builder:lint", 127 | "options": { 128 | "lintFilePatterns": [ 129 | "src/**/*.ts", 130 | "src/**/*.html" 131 | ] 132 | } 133 | }, 134 | "e2e": { 135 | "builder": "@angular-devkit/build-angular:protractor", 136 | "options": { 137 | "protractorConfig": "e2e/protractor.conf.js", 138 | "devServerTarget": "app:serve" 139 | }, 140 | "configurations": { 141 | "production": { 142 | "devServerTarget": "app:serve:production" 143 | }, 144 | "ci": { 145 | "devServerTarget": "app:serve:ci" 146 | } 147 | } 148 | }, 149 | "ionic-cordova-build": { 150 | "builder": "@ionic/angular-toolkit:cordova-build", 151 | "options": { 152 | "browserTarget": "app:build" 153 | }, 154 | "configurations": { 155 | "production": { 156 | "browserTarget": "app:build:production" 157 | } 158 | } 159 | }, 160 | "ionic-cordova-serve": { 161 | "builder": "@ionic/angular-toolkit:cordova-serve", 162 | "options": { 163 | "cordovaBuildTarget": "app:ionic-cordova-build", 164 | "devServerTarget": "app:serve" 165 | }, 166 | "configurations": { 167 | "production": { 168 | "cordovaBuildTarget": "app:ionic-cordova-build:production", 169 | "devServerTarget": "app:serve:production" 170 | } 171 | } 172 | }, 173 | "deploy": { 174 | "builder": "@angular/fire:deploy", 175 | "options": {} 176 | } 177 | } 178 | } 179 | }, 180 | "cli": { 181 | "defaultCollection": "@ionic/angular-toolkit" 182 | }, 183 | "schematics": { 184 | "@ionic/angular-toolkit:component": { 185 | "styleext": "scss" 186 | }, 187 | "@ionic/angular-toolkit:page": { 188 | "styleext": "scss" 189 | } 190 | } 191 | } -------------------------------------------------------------------------------- /capacitor.config.ts: -------------------------------------------------------------------------------- 1 | import { CapacitorConfig } from '@capacitor/cli'; 2 | 3 | const config: CapacitorConfig = { 4 | appId: 'io.ionic.starter', 5 | appName: 'firebase-database-tutorial', 6 | webDir: 'www', 7 | bundledWebRuntime: false 8 | }; 9 | 10 | export default config; 11 | -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | browserName: 'chrome' 17 | }, 18 | directConnect: true, 19 | SELENIUM_PROMISE_MANAGER: false, 20 | baseUrl: 'http://localhost:4200/', 21 | framework: 'jasmine', 22 | jasmineNodeOpts: { 23 | showColors: true, 24 | defaultTimeoutInterval: 30000, 25 | print: function() {} 26 | }, 27 | onPrepare() { 28 | require('ts-node').register({ 29 | project: require('path').join(__dirname, './tsconfig.json') 30 | }); 31 | jasmine.getEnv().addReporter(new SpecReporter({ 32 | spec: { 33 | displayStacktrace: StacktraceOption.PRETTY 34 | } 35 | })); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('new App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should be blank', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toContain('Start with Ionic UI Components'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.deepCss('app-root ion-content')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es2018", 7 | "types": [ 8 | "jasmine", 9 | "node" 10 | ] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": [ 3 | { 4 | "target": "app", 5 | "public": "www", 6 | "ignore": [ 7 | "**/.*" 8 | ], 9 | "headers": [ 10 | { 11 | "source": "*.[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].+(css|js)", 12 | "headers": [ 13 | { 14 | "key": "Cache-Control", 15 | "value": "public,max-age=31536000,immutable" 16 | } 17 | ] 18 | } 19 | ], 20 | "rewrites": [ 21 | { 22 | "source": "**", 23 | "destination": "/index.html" 24 | } 25 | ] 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /ionic.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "firebase-database-tutorial", 3 | "integrations": { 4 | "capacitor": {} 5 | }, 6 | "type": "angular" 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 | -------------------------------------------------------------------------------- /ios/App/App.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2FAD9763203C412B000D30F8 /* config.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAD9762203C412B000D30F8 /* config.xml */; }; 11 | 50379B232058CBB4000EE86E /* capacitor.config.json in Resources */ = {isa = PBXBuildFile; fileRef = 50379B222058CBB4000EE86E /* capacitor.config.json */; }; 12 | 504EC3081FED79650016851F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504EC3071FED79650016851F /* AppDelegate.swift */; }; 13 | 504EC30D1FED79650016851F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30B1FED79650016851F /* Main.storyboard */; }; 14 | 504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; }; 15 | 504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; }; 16 | 50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; }; 17 | A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */; }; 18 | B3029E1A2714E98400B077C7 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B3029E192714E98400B077C7 /* GoogleService-Info.plist */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | 2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = ""; }; 23 | 50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = ""; }; 24 | 504EC3041FED79650016851F /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | 504EC3071FED79650016851F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 26 | 504EC30C1FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 27 | 504EC30E1FED79650016851F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 28 | 504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 29 | 504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 30 | 50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = ""; }; 31 | AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = ""; }; 33 | B3029E192714E98400B077C7 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 34 | FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = ""; }; 35 | /* End PBXFileReference section */ 36 | 37 | /* Begin PBXFrameworksBuildPhase section */ 38 | 504EC3011FED79650016851F /* Frameworks */ = { 39 | isa = PBXFrameworksBuildPhase; 40 | buildActionMask = 2147483647; 41 | files = ( 42 | A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */, 43 | ); 44 | runOnlyForDeploymentPostprocessing = 0; 45 | }; 46 | /* End PBXFrameworksBuildPhase section */ 47 | 48 | /* Begin PBXGroup section */ 49 | 27E2DDA53C4D2A4D1A88CE4A /* Frameworks */ = { 50 | isa = PBXGroup; 51 | children = ( 52 | AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */, 53 | ); 54 | name = Frameworks; 55 | sourceTree = ""; 56 | }; 57 | 504EC2FB1FED79650016851F = { 58 | isa = PBXGroup; 59 | children = ( 60 | 504EC3061FED79650016851F /* App */, 61 | 504EC3051FED79650016851F /* Products */, 62 | 7F8756D8B27F46E3366F6CEA /* Pods */, 63 | 27E2DDA53C4D2A4D1A88CE4A /* Frameworks */, 64 | ); 65 | sourceTree = ""; 66 | }; 67 | 504EC3051FED79650016851F /* Products */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | 504EC3041FED79650016851F /* App.app */, 71 | ); 72 | name = Products; 73 | sourceTree = ""; 74 | }; 75 | 504EC3061FED79650016851F /* App */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | B3029E192714E98400B077C7 /* GoogleService-Info.plist */, 79 | 50379B222058CBB4000EE86E /* capacitor.config.json */, 80 | 504EC3071FED79650016851F /* AppDelegate.swift */, 81 | 504EC30B1FED79650016851F /* Main.storyboard */, 82 | 504EC30E1FED79650016851F /* Assets.xcassets */, 83 | 504EC3101FED79650016851F /* LaunchScreen.storyboard */, 84 | 504EC3131FED79650016851F /* Info.plist */, 85 | 2FAD9762203C412B000D30F8 /* config.xml */, 86 | 50B271D01FEDC1A000F3C39B /* public */, 87 | ); 88 | path = App; 89 | sourceTree = ""; 90 | }; 91 | 7F8756D8B27F46E3366F6CEA /* Pods */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */, 95 | AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */, 96 | ); 97 | name = Pods; 98 | sourceTree = ""; 99 | }; 100 | /* End PBXGroup section */ 101 | 102 | /* Begin PBXNativeTarget section */ 103 | 504EC3031FED79650016851F /* App */ = { 104 | isa = PBXNativeTarget; 105 | buildConfigurationList = 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */; 106 | buildPhases = ( 107 | 6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */, 108 | 504EC3001FED79650016851F /* Sources */, 109 | 504EC3011FED79650016851F /* Frameworks */, 110 | 504EC3021FED79650016851F /* Resources */, 111 | 9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */, 112 | ); 113 | buildRules = ( 114 | ); 115 | dependencies = ( 116 | ); 117 | name = App; 118 | productName = App; 119 | productReference = 504EC3041FED79650016851F /* App.app */; 120 | productType = "com.apple.product-type.application"; 121 | }; 122 | /* End PBXNativeTarget section */ 123 | 124 | /* Begin PBXProject section */ 125 | 504EC2FC1FED79650016851F /* Project object */ = { 126 | isa = PBXProject; 127 | attributes = { 128 | LastSwiftUpdateCheck = 0920; 129 | LastUpgradeCheck = 0920; 130 | TargetAttributes = { 131 | 504EC3031FED79650016851F = { 132 | CreatedOnToolsVersion = 9.2; 133 | LastSwiftMigration = 1100; 134 | ProvisioningStyle = Automatic; 135 | }; 136 | }; 137 | }; 138 | buildConfigurationList = 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */; 139 | compatibilityVersion = "Xcode 8.0"; 140 | developmentRegion = en; 141 | hasScannedForEncodings = 0; 142 | knownRegions = ( 143 | en, 144 | Base, 145 | ); 146 | mainGroup = 504EC2FB1FED79650016851F; 147 | productRefGroup = 504EC3051FED79650016851F /* Products */; 148 | projectDirPath = ""; 149 | projectRoot = ""; 150 | targets = ( 151 | 504EC3031FED79650016851F /* App */, 152 | ); 153 | }; 154 | /* End PBXProject section */ 155 | 156 | /* Begin PBXResourcesBuildPhase section */ 157 | 504EC3021FED79650016851F /* Resources */ = { 158 | isa = PBXResourcesBuildPhase; 159 | buildActionMask = 2147483647; 160 | files = ( 161 | 504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */, 162 | 50B271D11FEDC1A000F3C39B /* public in Resources */, 163 | 504EC30F1FED79650016851F /* Assets.xcassets in Resources */, 164 | 50379B232058CBB4000EE86E /* capacitor.config.json in Resources */, 165 | 504EC30D1FED79650016851F /* Main.storyboard in Resources */, 166 | 2FAD9763203C412B000D30F8 /* config.xml in Resources */, 167 | B3029E1A2714E98400B077C7 /* GoogleService-Info.plist in Resources */, 168 | ); 169 | runOnlyForDeploymentPostprocessing = 0; 170 | }; 171 | /* End PBXResourcesBuildPhase section */ 172 | 173 | /* Begin PBXShellScriptBuildPhase section */ 174 | 6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */ = { 175 | isa = PBXShellScriptBuildPhase; 176 | buildActionMask = 2147483647; 177 | files = ( 178 | ); 179 | inputPaths = ( 180 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 181 | "${PODS_ROOT}/Manifest.lock", 182 | ); 183 | name = "[CP] Check Pods Manifest.lock"; 184 | outputPaths = ( 185 | "$(DERIVED_FILE_DIR)/Pods-App-checkManifestLockResult.txt", 186 | ); 187 | runOnlyForDeploymentPostprocessing = 0; 188 | shellPath = /bin/sh; 189 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 190 | showEnvVarsInLog = 0; 191 | }; 192 | 9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */ = { 193 | isa = PBXShellScriptBuildPhase; 194 | buildActionMask = 2147483647; 195 | files = ( 196 | ); 197 | inputPaths = ( 198 | ); 199 | name = "[CP] Embed Pods Frameworks"; 200 | outputPaths = ( 201 | ); 202 | runOnlyForDeploymentPostprocessing = 0; 203 | shellPath = /bin/sh; 204 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-App/Pods-App-frameworks.sh\"\n"; 205 | showEnvVarsInLog = 0; 206 | }; 207 | /* End PBXShellScriptBuildPhase section */ 208 | 209 | /* Begin PBXSourcesBuildPhase section */ 210 | 504EC3001FED79650016851F /* Sources */ = { 211 | isa = PBXSourcesBuildPhase; 212 | buildActionMask = 2147483647; 213 | files = ( 214 | 504EC3081FED79650016851F /* AppDelegate.swift in Sources */, 215 | ); 216 | runOnlyForDeploymentPostprocessing = 0; 217 | }; 218 | /* End PBXSourcesBuildPhase section */ 219 | 220 | /* Begin PBXVariantGroup section */ 221 | 504EC30B1FED79650016851F /* Main.storyboard */ = { 222 | isa = PBXVariantGroup; 223 | children = ( 224 | 504EC30C1FED79650016851F /* Base */, 225 | ); 226 | name = Main.storyboard; 227 | sourceTree = ""; 228 | }; 229 | 504EC3101FED79650016851F /* LaunchScreen.storyboard */ = { 230 | isa = PBXVariantGroup; 231 | children = ( 232 | 504EC3111FED79650016851F /* Base */, 233 | ); 234 | name = LaunchScreen.storyboard; 235 | sourceTree = ""; 236 | }; 237 | /* End PBXVariantGroup section */ 238 | 239 | /* Begin XCBuildConfiguration section */ 240 | 504EC3141FED79650016851F /* Debug */ = { 241 | isa = XCBuildConfiguration; 242 | buildSettings = { 243 | ALWAYS_SEARCH_USER_PATHS = NO; 244 | CLANG_ANALYZER_NONNULL = YES; 245 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 246 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 247 | CLANG_CXX_LIBRARY = "libc++"; 248 | CLANG_ENABLE_MODULES = YES; 249 | CLANG_ENABLE_OBJC_ARC = YES; 250 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 251 | CLANG_WARN_BOOL_CONVERSION = YES; 252 | CLANG_WARN_COMMA = YES; 253 | CLANG_WARN_CONSTANT_CONVERSION = YES; 254 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 255 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 256 | CLANG_WARN_EMPTY_BODY = YES; 257 | CLANG_WARN_ENUM_CONVERSION = YES; 258 | CLANG_WARN_INFINITE_RECURSION = YES; 259 | CLANG_WARN_INT_CONVERSION = YES; 260 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 261 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 262 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 263 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 264 | CLANG_WARN_STRICT_PROTOTYPES = YES; 265 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 266 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 267 | CLANG_WARN_UNREACHABLE_CODE = YES; 268 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 269 | CODE_SIGN_IDENTITY = "iPhone Developer"; 270 | COPY_PHASE_STRIP = NO; 271 | DEBUG_INFORMATION_FORMAT = dwarf; 272 | ENABLE_STRICT_OBJC_MSGSEND = YES; 273 | ENABLE_TESTABILITY = YES; 274 | GCC_C_LANGUAGE_STANDARD = gnu11; 275 | GCC_DYNAMIC_NO_PIC = NO; 276 | GCC_NO_COMMON_BLOCKS = YES; 277 | GCC_OPTIMIZATION_LEVEL = 0; 278 | GCC_PREPROCESSOR_DEFINITIONS = ( 279 | "DEBUG=1", 280 | "$(inherited)", 281 | ); 282 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 283 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 284 | GCC_WARN_UNDECLARED_SELECTOR = YES; 285 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 286 | GCC_WARN_UNUSED_FUNCTION = YES; 287 | GCC_WARN_UNUSED_VARIABLE = YES; 288 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 289 | MTL_ENABLE_DEBUG_INFO = YES; 290 | ONLY_ACTIVE_ARCH = YES; 291 | SDKROOT = iphoneos; 292 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 293 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 294 | }; 295 | name = Debug; 296 | }; 297 | 504EC3151FED79650016851F /* Release */ = { 298 | isa = XCBuildConfiguration; 299 | buildSettings = { 300 | ALWAYS_SEARCH_USER_PATHS = NO; 301 | CLANG_ANALYZER_NONNULL = YES; 302 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 303 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 304 | CLANG_CXX_LIBRARY = "libc++"; 305 | CLANG_ENABLE_MODULES = YES; 306 | CLANG_ENABLE_OBJC_ARC = YES; 307 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 308 | CLANG_WARN_BOOL_CONVERSION = YES; 309 | CLANG_WARN_COMMA = YES; 310 | CLANG_WARN_CONSTANT_CONVERSION = YES; 311 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 312 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 313 | CLANG_WARN_EMPTY_BODY = YES; 314 | CLANG_WARN_ENUM_CONVERSION = YES; 315 | CLANG_WARN_INFINITE_RECURSION = YES; 316 | CLANG_WARN_INT_CONVERSION = YES; 317 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 318 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 319 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 320 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 321 | CLANG_WARN_STRICT_PROTOTYPES = YES; 322 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 323 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 324 | CLANG_WARN_UNREACHABLE_CODE = YES; 325 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 326 | CODE_SIGN_IDENTITY = "iPhone Developer"; 327 | COPY_PHASE_STRIP = NO; 328 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 329 | ENABLE_NS_ASSERTIONS = NO; 330 | ENABLE_STRICT_OBJC_MSGSEND = YES; 331 | GCC_C_LANGUAGE_STANDARD = gnu11; 332 | GCC_NO_COMMON_BLOCKS = YES; 333 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 334 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 335 | GCC_WARN_UNDECLARED_SELECTOR = YES; 336 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 337 | GCC_WARN_UNUSED_FUNCTION = YES; 338 | GCC_WARN_UNUSED_VARIABLE = YES; 339 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 340 | MTL_ENABLE_DEBUG_INFO = NO; 341 | SDKROOT = iphoneos; 342 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 343 | VALIDATE_PRODUCT = YES; 344 | }; 345 | name = Release; 346 | }; 347 | 504EC3171FED79650016851F /* Debug */ = { 348 | isa = XCBuildConfiguration; 349 | baseConfigurationReference = FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */; 350 | buildSettings = { 351 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 352 | CODE_SIGN_STYLE = Automatic; 353 | DEVELOPMENT_TEAM = Z42BAMC5G7; 354 | INFOPLIST_FILE = App/Info.plist; 355 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 356 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 357 | OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; 358 | PRODUCT_BUNDLE_IDENTIFIER = com.example.app; 359 | PRODUCT_NAME = "$(TARGET_NAME)"; 360 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 361 | SWIFT_VERSION = 5.0; 362 | TARGETED_DEVICE_FAMILY = "1,2"; 363 | }; 364 | name = Debug; 365 | }; 366 | 504EC3181FED79650016851F /* Release */ = { 367 | isa = XCBuildConfiguration; 368 | baseConfigurationReference = AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */; 369 | buildSettings = { 370 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 371 | CODE_SIGN_STYLE = Automatic; 372 | DEVELOPMENT_TEAM = Z42BAMC5G7; 373 | INFOPLIST_FILE = App/Info.plist; 374 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 375 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 376 | PRODUCT_BUNDLE_IDENTIFIER = com.example.app; 377 | PRODUCT_NAME = "$(TARGET_NAME)"; 378 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; 379 | SWIFT_VERSION = 5.0; 380 | TARGETED_DEVICE_FAMILY = "1,2"; 381 | }; 382 | name = Release; 383 | }; 384 | /* End XCBuildConfiguration section */ 385 | 386 | /* Begin XCConfigurationList section */ 387 | 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */ = { 388 | isa = XCConfigurationList; 389 | buildConfigurations = ( 390 | 504EC3141FED79650016851F /* Debug */, 391 | 504EC3151FED79650016851F /* Release */, 392 | ); 393 | defaultConfigurationIsVisible = 0; 394 | defaultConfigurationName = Release; 395 | }; 396 | 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */ = { 397 | isa = XCConfigurationList; 398 | buildConfigurations = ( 399 | 504EC3171FED79650016851F /* Debug */, 400 | 504EC3181FED79650016851F /* Release */, 401 | ); 402 | defaultConfigurationIsVisible = 0; 403 | defaultConfigurationName = Release; 404 | }; 405 | /* End XCConfigurationList section */ 406 | }; 407 | rootObject = 504EC2FC1FED79650016851F /* Project object */; 408 | } 409 | -------------------------------------------------------------------------------- /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 | override func touchesBegan(_ touches: Set, with event: UIEvent?) { 50 | super.touchesBegan(touches, with: event) 51 | 52 | let statusBarRect = UIApplication.shared.statusBarFrame 53 | guard let touchPoint = event?.allTouches?.first?.location(in: self.window) else { return } 54 | 55 | if statusBarRect.contains(touchPoint) { 56 | NotificationCenter.default.post(name: .capacitorStatusBarTapped, object: nil) 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/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/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CLIENT_ID 6 | 38298401556-t2vraeu6chbv544mcbb8rlsq2t5taq9g.apps.googleusercontent.com 7 | REVERSED_CLIENT_ID 8 | com.googleusercontent.apps.38298401556-t2vraeu6chbv544mcbb8rlsq2t5taq9g 9 | API_KEY 10 | AIzaSyCp5Xw68gTZ4ec6sHKt7cwXzVTlNQKWye4 11 | GCM_SENDER_ID 12 | 38298401556 13 | PLIST_VERSION 14 | 1 15 | BUNDLE_ID 16 | com.ionictutorial.app 17 | PROJECT_ID 18 | ionic-contacts-app 19 | STORAGE_BUCKET 20 | ionic-contacts-app.appspot.com 21 | IS_ADS_ENABLED 22 | 23 | IS_ANALYTICS_ENABLED 24 | 25 | IS_APPINVITE_ENABLED 26 | 27 | IS_GCM_ENABLED 28 | 29 | IS_SIGNIN_ENABLED 30 | 31 | GOOGLE_APP_ID 32 | 1:38298401556:ios:6d7ae9f9fe62aa2074a001 33 | 34 | -------------------------------------------------------------------------------- /ios/App/App/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | firebase-file-storage-tutorial 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 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSAllowsArbitraryLoads 28 | 29 | 30 | UILaunchStoryboardName 31 | LaunchScreen 32 | UIMainStoryboardFile 33 | Main 34 | UIRequiredDeviceCapabilities 35 | 36 | armv7 37 | 38 | UISupportedInterfaceOrientations 39 | 40 | UIInterfaceOrientationPortrait 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UISupportedInterfaceOrientations~ipad 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationPortraitUpsideDown 48 | UIInterfaceOrientationLandscapeLeft 49 | UIInterfaceOrientationLandscapeRight 50 | 51 | UIViewControllerBasedStatusBarAppearance 52 | 53 | NSCameraUsageDescription 54 | Privacy - Camera Usage Description 55 | NSPhotoLibraryAddUsageDescription 56 | Privacy - Photo Library Additions Usage Description 57 | NSPhotoLibraryUsageDescription 58 | Privacy - Photo Library Usage Description 59 | 60 | 61 | -------------------------------------------------------------------------------- /ios/App/App/capacitor.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "appId": "io.ionic.starter", 3 | "appName": "firebase-database-tutorial", 4 | "webDir": "www", 5 | "bundledWebRuntime": false 6 | } 7 | -------------------------------------------------------------------------------- /ios/App/App/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ios/App/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '12.0' 2 | use_frameworks! 3 | 4 | # workaround to avoid Xcode caching of Pods that requires 5 | # Product -> Clean Build Folder after new Cordova plugins installed 6 | # Requires CocoaPods 1.6 or newer 7 | install! 'cocoapods', :disable_input_output_paths => true 8 | 9 | def capacitor_pods 10 | pod 'Capacitor', :path => '../../node_modules/@capacitor/ios' 11 | pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios' 12 | pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app' 13 | pod 'CapacitorCamera', :path => '../../node_modules/@capacitor/camera' 14 | pod 'CapacitorFilesystem', :path => '../../node_modules/@capacitor/filesystem' 15 | pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics' 16 | pod 'CapacitorKeyboard', :path => '../../node_modules/@capacitor/keyboard' 17 | pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar' 18 | pod 'CapacitorStorage', :path => '../../node_modules/@capacitor/storage' 19 | end 20 | 21 | target 'App' do 22 | capacitor_pods 23 | # Add your Pods here 24 | end 25 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | jasmine: { 17 | // you can add configuration options for Jasmine here 18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html 19 | // for example, you can disable the random execution with `random: false` 20 | // or set a specific seed with `seed: 4321` 21 | }, 22 | clearContext: false // leave Jasmine Spec Runner output visible in browser 23 | }, 24 | jasmineHtmlReporter: { 25 | suppressAll: true // removes the duplicated traces 26 | }, 27 | coverageReporter: { 28 | dir: require('path').join(__dirname, './coverage/ngv'), 29 | subdir: '.', 30 | reporters: [ 31 | { type: 'html' }, 32 | { type: 'text-summary' } 33 | ] 34 | }, 35 | reporters: ['progress', 'kjhtml'], 36 | port: 9876, 37 | colors: true, 38 | logLevel: config.LOG_INFO, 39 | autoWatch: true, 40 | browsers: ['Chrome'], 41 | singleRun: false, 42 | restartOnFileChange: true 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ionic-firebase-storage-tutorial", 3 | "version": "2.0.0", 4 | "author": "IonicThemes Team", 5 | "homepage": "https://ionicthemes.com/", 6 | "scripts": { 7 | "ng": "ng", 8 | "start": "ng serve", 9 | "build": "ng build", 10 | "test": "ng test", 11 | "lint": "ng lint", 12 | "e2e": "ng e2e" 13 | }, 14 | "private": true, 15 | "dependencies": { 16 | "@angular/common": "~12.1.1", 17 | "@angular/core": "~12.1.1", 18 | "@angular/fire": "^7.0.4", 19 | "@angular/forms": "~12.1.1", 20 | "@angular/platform-browser": "~12.1.1", 21 | "@angular/platform-browser-dynamic": "~12.1.1", 22 | "@angular/router": "~12.1.1", 23 | "@capacitor/android": "^3.2.4", 24 | "@capacitor/app": "1.0.3", 25 | "@capacitor/camera": "^1.1.0", 26 | "@capacitor/core": "3.2.4", 27 | "@capacitor/filesystem": "^1.0.3", 28 | "@capacitor/haptics": "1.1.0", 29 | "@capacitor/ios": "^3.2.4", 30 | "@capacitor/keyboard": "1.1.0", 31 | "@capacitor/status-bar": "1.0.3", 32 | "@capacitor/storage": "^1.2.0", 33 | "@ionic/angular": "^5.8.1", 34 | "@ionic/pwa-elements": "^3.0.2", 35 | "firebase": "^9.1.0", 36 | "rxfire": "^6.0.0", 37 | "rxjs": "~6.6.0", 38 | "tslib": "^2.2.0", 39 | "zone.js": "~0.11.4" 40 | }, 41 | "devDependencies": { 42 | "@angular-devkit/architect": "^0.1200.0", 43 | "@angular-devkit/build-angular": "~12.1.1", 44 | "@angular-eslint/builder": "~12.0.0", 45 | "@angular-eslint/eslint-plugin": "~12.0.0", 46 | "@angular-eslint/eslint-plugin-template": "~12.0.0", 47 | "@angular-eslint/template-parser": "~12.0.0", 48 | "@angular/cli": "~12.1.1", 49 | "@angular/compiler": "~12.1.1", 50 | "@angular/compiler-cli": "~12.1.1", 51 | "@angular/language-service": "~12.0.1", 52 | "@capacitor/cli": "3.2.4", 53 | "@ionic/angular-toolkit": "^4.0.0", 54 | "@types/jasmine": "~3.6.0", 55 | "@types/jasminewd2": "~2.0.3", 56 | "@types/node": "^12.20.27", 57 | "@typescript-eslint/eslint-plugin": "4.16.1", 58 | "@typescript-eslint/parser": "4.16.1", 59 | "eslint": "^7.6.0", 60 | "eslint-plugin-import": "2.22.1", 61 | "eslint-plugin-jsdoc": "30.7.6", 62 | "eslint-plugin-prefer-arrow": "1.2.2", 63 | "firebase-tools": "^9.19.0", 64 | "fuzzy": "^0.1.3", 65 | "inquirer": "^6.2.2", 66 | "inquirer-autocomplete-prompt": "^1.0.1", 67 | "jasmine-core": "~3.8.0", 68 | "jasmine-spec-reporter": "~5.0.0", 69 | "jsonc-parser": "^3.0.0", 70 | "karma": "~6.3.2", 71 | "karma-chrome-launcher": "~3.1.0", 72 | "karma-coverage": "~2.0.3", 73 | "karma-coverage-istanbul-reporter": "~3.0.2", 74 | "karma-jasmine": "~4.0.0", 75 | "karma-jasmine-html-reporter": "^1.5.0", 76 | "open": "^7.0.3", 77 | "protractor": "~7.0.0", 78 | "ts-node": "~8.3.0", 79 | "typescript": "~4.2.4" 80 | }, 81 | "description": "Ionic Firebase Storage Tutorial" 82 | } 83 | -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { PreloadAllModules, RouterModule, Routes } from '@angular/router'; 3 | 4 | const routes: Routes = [ 5 | { 6 | path: '', 7 | loadChildren: () => import('./tabs/tabs.module').then(m => m.TabsPageModule) 8 | } 9 | ]; 10 | 11 | @NgModule({ 12 | imports: [ 13 | RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules }) 14 | ], 15 | exports: [RouterModule] 16 | }) 17 | export class AppRoutingModule { } 18 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/src/app/app.component.scss -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; 2 | import { TestBed, waitForAsync } from '@angular/core/testing'; 3 | 4 | import { AppComponent } from './app.component'; 5 | 6 | describe('AppComponent', () => { 7 | 8 | beforeEach(waitForAsync(() => { 9 | 10 | TestBed.configureTestingModule({ 11 | declarations: [AppComponent], 12 | schemas: [CUSTOM_ELEMENTS_SCHEMA], 13 | }).compileComponents(); 14 | })); 15 | 16 | it('should create the app', () => { 17 | const fixture = TestBed.createComponent(AppComponent); 18 | const app = fixture.debugElement.componentInstance; 19 | expect(app).toBeTruthy(); 20 | }); 21 | // TODO: add more tests! 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: 'app.component.html', 6 | styleUrls: ['app.component.scss'], 7 | }) 8 | export class AppComponent { 9 | constructor() {} 10 | } 11 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import { RouteReuseStrategy } from '@angular/router'; 4 | import { IonicModule, IonicRouteStrategy } from '@ionic/angular'; 5 | import { AppComponent } from './app.component'; 6 | import { AppRoutingModule } from './app-routing.module'; 7 | import { environment } from 'src/environments/environment'; 8 | import { provideFirebaseApp, initializeApp } from '@angular/fire/app'; 9 | import { provideStorage, getStorage } from "@angular/fire/storage"; 10 | import { AngularFireAuthModule } from '@angular/fire/compat/auth'; 11 | import { AngularFireModule } from '@angular/fire/compat'; 12 | import { provideAuth } from '@angular/fire/auth'; 13 | import { whichAuth } from './utils/firebase-auth-helper'; 14 | 15 | @NgModule({ 16 | declarations: [AppComponent], 17 | imports: [ 18 | BrowserModule, 19 | IonicModule.forRoot(), 20 | AppRoutingModule, 21 | provideFirebaseApp(() => initializeApp(environment.firebase)), 22 | provideStorage(() => getStorage()), 23 | // Auth guards are not yet implemented in the new AngularFire so we need to load them from the 'old' modules 24 | // Issue: https://github.com/angular/angularfire/issues/2986# 25 | AngularFireModule.initializeApp(environment.firebase), 26 | AngularFireAuthModule, 27 | // Firebase auth needs to be initialized manually on iOS because the default getAuth() function assumes you're in a browser context and automatically includes web code that causes errors in iOS. 28 | // Issue https://github.com/firebase/firebase-js-sdk/issues/5019#issuecomment-861761505 29 | provideAuth(() => whichAuth) 30 | ], 31 | providers: [ 32 | { provide: RouteReuseStrategy, useClass: IonicRouteStrategy } 33 | ], 34 | bootstrap: [AppComponent] 35 | }) 36 | export class AppModule {} 37 | -------------------------------------------------------------------------------- /src/app/private-content/private-content.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { RouterModule } from '@angular/router'; 4 | import { PrivateContentPage } from './private-content.page'; 5 | import { PrivateContentResolver } from './private-content.resolver'; 6 | import { AngularFireAuthGuard, redirectUnauthorizedTo } from '@angular/fire/compat/auth-guard'; 7 | import { IonicModule } from '@ionic/angular'; 8 | import { ShellModule } from '../utils/shell/shell.module'; 9 | 10 | const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['/tabs/tab2']); 11 | 12 | @NgModule({ 13 | declarations: [PrivateContentPage], 14 | imports: [ 15 | CommonModule, 16 | IonicModule, 17 | ShellModule, 18 | RouterModule.forChild([ 19 | { 20 | path: '', 21 | component: PrivateContentPage, 22 | canActivate: [AngularFireAuthGuard], 23 | data: { 24 | authGuardPipe: redirectUnauthorizedToLogin 25 | }, 26 | resolve: { 27 | data: PrivateContentResolver 28 | } 29 | } 30 | ]) 31 | ], 32 | providers: [PrivateContentResolver] 33 | }) 34 | export class PrivateContentModule { } 35 | -------------------------------------------------------------------------------- /src/app/private-content/private-content.page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Private Content 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Private Content 13 | 14 | 15 | 16 | 17 |

This page allows you to list and upload files that only you can access.

18 |
19 |
20 | 21 | 22 | 23 | 24 | 25 | Take Photo 26 | 27 | 28 | 29 | Log out 30 | 31 | 32 | 33 | 34 | 35 |

We couldn't find any files related to your account. Please take a photo and we will upload it to your personal storage folder.

36 |
37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
49 | -------------------------------------------------------------------------------- /src/app/private-content/private-content.page.scss: -------------------------------------------------------------------------------- 1 | ion-content ion-toolbar { 2 | --background: translucent; 3 | } 4 | 5 | ion-grid { 6 | --ion-grid-padding: 10px; 7 | } 8 | 9 | .copy-row { 10 | padding: 10px; 11 | } 12 | 13 | .section-title { 14 | font-weight: bold; 15 | } 16 | 17 | .photo-item { 18 | app-image-shell { 19 | --image-shell-border-radius: 6px; 20 | --image-shell-loading-background: #e1ecff; 21 | } 22 | } 23 | 24 | .buttons-row { 25 | padding-left: 10px; 26 | padding-right: 10px; 27 | } 28 | 29 | .empty-state-copy { 30 | text-align: center; 31 | background: #e1ecff; 32 | padding: 20px; 33 | border-radius: 10px; 34 | font-size: 18px; 35 | color: #1e1e1e; 36 | } 37 | -------------------------------------------------------------------------------- /src/app/private-content/private-content.page.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { PrivateContentPage } from './private-content.page'; 5 | 6 | describe('PrivateContentPage', () => { 7 | let component: PrivateContentPage; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(waitForAsync(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ PrivateContentPage ], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(PrivateContentPage); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/private-content/private-content.page.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnDestroy, OnInit } from '@angular/core'; 2 | import { ActivatedRoute, Router } from '@angular/router'; 3 | import { Camera, CameraResultType, CameraSource } from '@capacitor/camera'; 4 | import { ActionSheetController } from '@ionic/angular'; 5 | import { Subscription } from 'rxjs'; 6 | import { map } from 'rxjs/operators'; 7 | import { ImageListingModel } from '../utils/models/image-listing.model'; 8 | import { DataService } from '../utils/services/data.service'; 9 | 10 | @Component({ 11 | selector: 'app-private-content', 12 | templateUrl: './private-content.page.html', 13 | styleUrls: ['./private-content.page.scss'], 14 | }) 15 | export class PrivateContentPage implements OnInit, OnDestroy { 16 | 17 | files: ImageListingModel; 18 | private subs: Subscription[] = []; 19 | 20 | constructor( 21 | public dataService: DataService, 22 | public actionSheetController: ActionSheetController, 23 | private route: ActivatedRoute, 24 | private router: Router 25 | ) {} 26 | 27 | ngOnInit() { 28 | const sub1 = this.route.data 29 | .pipe( 30 | map((resolvedRouteData) => { 31 | const sub2 = resolvedRouteData['data'].state.subscribe( 32 | (dataModel: ImageListingModel) => { 33 | this.files = dataModel; 34 | } 35 | ); 36 | this.subs.push(sub2); 37 | }) 38 | ).subscribe(); 39 | 40 | this.subs.push(sub1); 41 | } 42 | 43 | ngOnDestroy(): void { 44 | this.subs.forEach((s) => s.unsubscribe()); 45 | } 46 | 47 | async openCameraComponent() { 48 | // Take a photo 49 | const capturedPhoto = await Camera.getPhoto({ 50 | resultType: CameraResultType.Base64, 51 | source: CameraSource.Prompt, // prompts the user to select either the photo album or take a photo. 52 | quality: 90 // highest quality (0 to 100) 53 | }); 54 | 55 | const savedImageFile = await this.dataService.savePictureInFirebaseStorage(capturedPhoto); 56 | this.files.imagesUrls.unshift(savedImageFile); 57 | } 58 | 59 | public async showActionSheet(photo: string, position: number) { 60 | const actionSheet = await this.actionSheetController.create({ 61 | header: 'Photos', 62 | buttons: [{ 63 | text: 'Delete', 64 | role: 'destructive', 65 | icon: 'trash', 66 | handler: () => { 67 | this.dataService.deletePicture(photo, position) 68 | .then((x) => { 69 | // File deleted successfully 70 | this.files.imagesUrls.splice(position, 1); 71 | }).catch((error) => { 72 | // Uh-oh, an error occurred! 73 | }); 74 | } 75 | }, { 76 | text: 'Cancel', 77 | icon: 'close', 78 | role: 'cancel', 79 | handler: () => { 80 | // Nothing to do, action sheet is automatically closed 81 | } 82 | }] 83 | }); 84 | await actionSheet.present(); 85 | } 86 | 87 | signOut() { 88 | this.dataService.signOutFromFirebase() 89 | .then(() => this.router.navigate(['/tabs/tab2'])); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/app/private-content/private-content.resolver.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Resolve } from '@angular/router'; 3 | import { Observable } from 'rxjs'; 4 | import { ImageListingModel } from '../utils/models/image-listing.model'; 5 | import { DataService } from '../utils/services/data.service'; 6 | import { DataStore } from '../utils/shell/data-store'; 7 | 8 | @Injectable() 9 | export class PrivateContentResolver implements Resolve { 10 | 11 | constructor(private dataService: DataService) {} 12 | 13 | resolve() { 14 | const filesDataSource: Observable = this.dataService.getPrivateFilesDataSource(); 15 | const filesDataStore: DataStore = this.dataService.getPrivateFilesDataStore(filesDataSource); 16 | 17 | return filesDataStore; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/app/tab1/tab1.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { IonicModule } from '@ionic/angular'; 4 | import { RouterModule } from '@angular/router'; 5 | import { Tab1Page } from './tab1.page'; 6 | import { ShellModule } from '../utils/shell/shell.module'; 7 | import { Tab1Resolver } from './tab1.resolver'; 8 | 9 | @NgModule({ 10 | imports: [ 11 | CommonModule, 12 | IonicModule, 13 | ShellModule, 14 | RouterModule.forChild([ 15 | { 16 | path: '', 17 | component: Tab1Page, 18 | resolve: { 19 | data: Tab1Resolver 20 | } 21 | } 22 | ]) 23 | ], 24 | declarations: [Tab1Page], 25 | providers: [Tab1Resolver] 26 | }) 27 | export class Tab1PageModule {} 28 | -------------------------------------------------------------------------------- /src/app/tab1/tab1.page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Public Images 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Ionic Tutorial 14 | 15 | 16 | 17 | 18 |

This demo app shows how to use Firebase Storage from an Ionic app.

19 |

Files can be either public or protected using security rules to determine who has access to them. This section includes public files. Tab2 shows how to upload and load protected files.

20 |
21 |
22 | 23 |

Posts

24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 | 33 | 34 |

Friends

35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
43 | 44 |
45 | -------------------------------------------------------------------------------- /src/app/tab1/tab1.page.scss: -------------------------------------------------------------------------------- 1 | ion-content ion-toolbar { 2 | --background: translucent; 3 | } 4 | 5 | ion-grid { 6 | --ion-grid-padding: 10px; 7 | } 8 | 9 | .copy-row { 10 | padding: 10px; 11 | } 12 | 13 | .section-title { 14 | font-weight: bold; 15 | } 16 | 17 | .post-item { 18 | app-image-shell { 19 | --image-shell-border-radius: 6px; 20 | --image-shell-loading-background: #e1ecff; 21 | } 22 | } 23 | 24 | .friends-row { 25 | --ion-grid-columns: 7; // We want to show three friends and a half. Each friend fills 2 cols => (3.5 * 2 = 7 cols) 26 | --ion-grid-column-padding: 0px; 27 | 28 | flex-wrap: nowrap; 29 | overflow-x: scroll; 30 | will-change: scroll-position; 31 | 32 | .friend-item { 33 | --col-item-width: 2; 34 | 35 | &:first-child { 36 | margin-inline-start: -10px; 37 | } 38 | 39 | flex: 0 0 calc(calc(var(--col-item-width) / var(--ion-grid-columns, 12)) * 100%); 40 | width: calc(calc(var(--col-item-width) / var(--ion-grid-columns, 12)) * 100%); 41 | max-width: calc(calc(var(--col-item-width) / var(--ion-grid-columns, 12)) * 100%); 42 | 43 | padding: 0px 10px; 44 | 45 | app-image-shell { 46 | --image-shell-border-radius: 50%; 47 | --image-shell-loading-background: #e1ecff; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/app/tab1/tab1.page.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { Tab1Page } from './tab1.page'; 5 | 6 | describe('Tab1Page', () => { 7 | let component: Tab1Page; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [Tab1Page], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(Tab1Page); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/tab1/tab1.page.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnDestroy, OnInit } from '@angular/core'; 2 | import { ActivatedRoute } from '@angular/router'; 3 | import { Subscription } from 'rxjs'; 4 | import { map } from 'rxjs/operators'; 5 | import { ImageListingModel } from '../utils/models/image-listing.model'; 6 | 7 | @Component({ 8 | selector: 'app-tab1', 9 | templateUrl: 'tab1.page.html', 10 | styleUrls: ['tab1.page.scss'] 11 | }) 12 | export class Tab1Page implements OnInit, OnDestroy { 13 | 14 | friends: ImageListingModel; 15 | posts: ImageListingModel; 16 | private subs: Subscription[] = []; 17 | 18 | constructor(private route: ActivatedRoute) {} 19 | 20 | ngOnInit() { 21 | const sub3 = this.route.data 22 | .pipe( 23 | map((resolvedRouteData) => { 24 | 25 | const friendsDataStore = resolvedRouteData['data'].friends; 26 | const postsDataStore = resolvedRouteData['data'].posts; 27 | 28 | const sub1 = friendsDataStore.state.subscribe( 29 | (dataModel: ImageListingModel) => this.friends = dataModel 30 | ); 31 | 32 | const sub2 = postsDataStore.state.subscribe( 33 | (dataModel: ImageListingModel) => this.posts = dataModel 34 | ); 35 | 36 | this.subs.push(sub1); 37 | this.subs.push(sub2); 38 | }) 39 | ).subscribe(); 40 | this.subs.push(sub3); 41 | } 42 | 43 | ngOnDestroy(): void { 44 | this.subs.forEach((s) => s.unsubscribe()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/app/tab1/tab1.resolver.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Resolve } from '@angular/router'; 3 | import { Observable } from 'rxjs'; 4 | import { DataStore } from '../utils/shell/data-store'; 5 | import { DataService } from '../utils/services/data.service'; 6 | import { ImageListingModel } from '../utils/models/image-listing.model'; 7 | 8 | @Injectable() 9 | export class Tab1Resolver implements Resolve { 10 | 11 | constructor(private dataService: DataService) {} 12 | 13 | resolve() { 14 | 15 | const friendsDataSource: Observable = this.dataService.getFriendsImagesDataSource(); 16 | const friendsDataStore: DataStore = this.dataService.getFriendsImagesDataStore(friendsDataSource); 17 | 18 | const postsDataSource: Observable = this.dataService.getPostsImagesDataSource(); 19 | const postsDataStore: DataStore = this.dataService.getPostsImagesDataStore(postsDataSource); 20 | 21 | return { friends: friendsDataStore, posts: postsDataStore }; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/app/tab2/tab2.module.ts: -------------------------------------------------------------------------------- 1 | import { IonicModule } from '@ionic/angular'; 2 | import { RouterModule } from '@angular/router'; 3 | import { NgModule } from '@angular/core'; 4 | import { CommonModule } from '@angular/common'; 5 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 6 | import { Tab2Page } from './tab2.page'; 7 | import { AngularFireAuthGuard } from '@angular/fire/compat/auth-guard'; 8 | import { map } from 'rxjs/operators'; 9 | 10 | // Firebase guard to redirect logged in private content page 11 | const redirectLoggedInToProfile = (next) => map(user => { 12 | if (user !== null) { 13 | return ['tabs/tab2/private-content']; 14 | } else { 15 | return true; 16 | } 17 | }); 18 | 19 | @NgModule({ 20 | imports: [ 21 | IonicModule, 22 | CommonModule, 23 | FormsModule, 24 | ReactiveFormsModule, 25 | RouterModule.forChild([ 26 | { 27 | path: '', 28 | component: Tab2Page, 29 | canActivate: [AngularFireAuthGuard], 30 | data: { authGuardPipe: redirectLoggedInToProfile } 31 | }, 32 | { 33 | path: 'private-content', 34 | loadChildren: () => import('../private-content/private-content.module').then(m => m.PrivateContentModule) 35 | } 36 | ]) 37 | ], 38 | declarations: [Tab2Page] 39 | }) 40 | export class Tab2PageModule {} 41 | -------------------------------------------------------------------------------- /src/app/tab2/tab2.page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Upload Files 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Upload Files 13 | 14 | 15 | 16 | 17 |

In order to upload protected files to Firebase Storage we first need to authenticate. This way, access to the files is enabled only for their owner. Understand Firebase security rules.

18 |
19 |
20 | 68 |
69 | -------------------------------------------------------------------------------- /src/app/tab2/tab2.page.scss: -------------------------------------------------------------------------------- 1 | ion-content ion-toolbar { 2 | --background: translucent; 3 | } 4 | 5 | .copy-row { 6 | padding: 10px; 7 | } 8 | 9 | .login-form { 10 | padding: 40px; 11 | } 12 | 13 | .login-btn { 14 | margin-top: 30px; 15 | } 16 | 17 | .error-container { 18 | .error-message { 19 | display: flex; 20 | align-items: center; 21 | color: var(--ion-color-danger); 22 | font-size: 14px; 23 | 24 | ion-icon { 25 | padding-inline-end: 8px; 26 | } 27 | } 28 | } 29 | .success-container { 30 | .success-message { 31 | display: flex; 32 | align-items: center; 33 | color: var(--ion-color-success); 34 | font-size: 14px; 35 | 36 | ion-icon { 37 | padding-inline-end: 8px; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/app/tab2/tab2.page.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { IonicModule } from '@ionic/angular'; 3 | 4 | import { Tab2Page } from './tab2.page'; 5 | 6 | describe('Tab2Page', () => { 7 | let component: Tab2Page; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [Tab2Page], 13 | imports: [IonicModule.forRoot()] 14 | }).compileComponents(); 15 | 16 | fixture = TestBed.createComponent(Tab2Page); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/tab2/tab2.page.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { FormControl, FormGroup, Validators } from '@angular/forms'; 3 | import { Router } from '@angular/router'; 4 | import { ViewWillEnter } from '@ionic/angular'; 5 | import { DataService } from '../utils/services/data.service'; 6 | 7 | @Component({ 8 | selector: 'app-tab2', 9 | templateUrl: 'tab2.page.html', 10 | styleUrls: ['tab2.page.scss'] 11 | }) 12 | export class Tab2Page implements ViewWillEnter { 13 | loginForm: FormGroup; 14 | submitError: string; 15 | submitSuccess: string; 16 | 17 | validation_messages = { 18 | 'email': [ 19 | { type: 'required', message: 'Email is required.' }, 20 | { type: 'pattern', message: 'Enter a valid email.' } 21 | ], 22 | 'password': [ 23 | { type: 'required', message: 'Password is required.' }, 24 | { type: 'minlength', message: 'Password must be at least 5 characters long.' } 25 | ] 26 | }; 27 | 28 | constructor( 29 | public dataService: DataService, 30 | public router: Router 31 | ) { 32 | this.loginForm = new FormGroup({ 33 | 'email': new FormControl('', Validators.compose([ 34 | Validators.required, 35 | Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$') 36 | ])), 37 | 'password': new FormControl('', Validators.compose([ 38 | Validators.minLength(5), 39 | Validators.required 40 | ])) 41 | }); 42 | } 43 | 44 | ionViewWillEnter(): void { 45 | // empty the form success message 46 | this.submitSuccess = ""; 47 | } 48 | 49 | doSignIn(): void { 50 | this.submitError = null; 51 | const email = this.loginForm.value['email']; 52 | const password = this.loginForm.value['password']; 53 | 54 | this.dataService.signInToFirebase(email, password) 55 | .then(user => { 56 | this.submitSuccess = "User signed in successfully."; 57 | this.router.navigate(['tabs/tab2/private-content']); 58 | }) 59 | .catch(error => { 60 | this.submitError = error.message; 61 | }); 62 | } 63 | 64 | doSignUp(): void { 65 | this.submitError = null; 66 | const email = this.loginForm.value['email']; 67 | const password = this.loginForm.value['password']; 68 | 69 | this.dataService.signUpToFirebase(email, password) 70 | .then(user => { 71 | // automatically sign the user in 72 | this.dataService.signInToFirebase(email, password) 73 | .then(user => { 74 | this.router.navigate(['tabs/tab2/private-content']); 75 | }) 76 | }) 77 | .catch(error => { 78 | this.submitError = error.message; 79 | }); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/app/tabs/tabs-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import { TabsPage } from './tabs.page'; 4 | 5 | const routes: Routes = [ 6 | { 7 | path: 'tabs', 8 | component: TabsPage, 9 | children: [ 10 | { 11 | path: 'tab1', 12 | children: [ 13 | { 14 | path: '', 15 | loadChildren: () => 16 | import('../tab1/tab1.module').then(m => m.Tab1PageModule) 17 | } 18 | ] 19 | }, 20 | { 21 | path: 'tab2', 22 | children: [ 23 | { 24 | path: '', 25 | loadChildren: () => 26 | import('../tab2/tab2.module').then(m => m.Tab2PageModule) 27 | } 28 | ] 29 | }, 30 | { 31 | path: '', 32 | redirectTo: '/tabs/tab2', 33 | pathMatch: 'full' 34 | } 35 | ] 36 | }, 37 | { 38 | path: '', 39 | redirectTo: '/tabs/tab1', 40 | pathMatch: 'full' 41 | } 42 | ]; 43 | 44 | @NgModule({ 45 | imports: [RouterModule.forChild(routes)], 46 | exports: [RouterModule] 47 | }) 48 | export class TabsPageRoutingModule {} 49 | -------------------------------------------------------------------------------- /src/app/tabs/tabs.module.ts: -------------------------------------------------------------------------------- 1 | import { IonicModule } from '@ionic/angular'; 2 | import { NgModule } from '@angular/core'; 3 | import { CommonModule } from '@angular/common'; 4 | import { FormsModule } from '@angular/forms'; 5 | 6 | import { TabsPageRoutingModule } from './tabs-routing.module'; 7 | 8 | import { TabsPage } from './tabs.page'; 9 | @NgModule({ 10 | imports: [ 11 | IonicModule, 12 | CommonModule, 13 | FormsModule, 14 | TabsPageRoutingModule 15 | ], 16 | declarations: [TabsPage] 17 | }) 18 | export class TabsPageModule {} 19 | -------------------------------------------------------------------------------- /src/app/tabs/tabs.page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Public content 7 | 8 | 9 | 10 | 11 | Private Content 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/app/tabs/tabs.page.scss: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/app/tabs/tabs.page.spec.ts: -------------------------------------------------------------------------------- 1 | import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | 4 | import { TabsPage } from './tabs.page'; 5 | 6 | describe('TabsPage', () => { 7 | let component: TabsPage; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [TabsPage], 13 | schemas: [CUSTOM_ELEMENTS_SCHEMA], 14 | }).compileComponents(); 15 | })); 16 | 17 | beforeEach(() => { 18 | fixture = TestBed.createComponent(TabsPage); 19 | component = fixture.componentInstance; 20 | fixture.detectChanges(); 21 | }); 22 | 23 | it('should create', () => { 24 | expect(component).toBeTruthy(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/app/tabs/tabs.page.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-tabs', 5 | templateUrl: 'tabs.page.html', 6 | styleUrls: ['tabs.page.scss'] 7 | }) 8 | export class TabsPage { 9 | 10 | constructor() {} 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/app/utils/firebase-auth-helper.ts: -------------------------------------------------------------------------------- 1 | import { Capacitor } from "@capacitor/core"; 2 | import { getApp } from "firebase/app"; 3 | import { Auth, getAuth, initializeAuth } from "firebase/auth"; 4 | 5 | let auth: Auth; 6 | // Firebase auth needs to be initialized manually on iOS because the default getAuth() function assumes you're in a browser context and automatically includes pieces you need to perform social login causing errors. 7 | // Issue https://github.com/firebase/firebase-js-sdk/issues/5019#issuecomment-861761505 8 | 9 | function selectAuthDependingOnEnv(): Auth { 10 | if (auth) { 11 | return auth; 12 | } 13 | else { 14 | // we wait on purpose 5 secs to make sure that firebase auth has initilized 15 | setTimeout(() => { 16 | if (Capacitor.isNativePlatform()) { 17 | auth = initializeAuth(getApp()); 18 | } else { 19 | try { 20 | auth = getAuth(); 21 | } catch (err) { 22 | console.log(err); 23 | } 24 | } 25 | }, 5000); 26 | return auth; 27 | } 28 | } 29 | 30 | export const whichAuth = selectAuthDependingOnEnv(); 31 | -------------------------------------------------------------------------------- /src/app/utils/models/image-listing.model.ts: -------------------------------------------------------------------------------- 1 | import { ShellModel } from '../shell/data-store'; 2 | 3 | export class ImageListingModel extends ShellModel { 4 | imagesUrls: string[] = [ 5 | "", 6 | "", 7 | "", 8 | "", 9 | "", 10 | "" 11 | ]; 12 | 13 | constructor() { 14 | super(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/app/utils/services/data.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { DataService } from './data.service'; 4 | 5 | describe('DataService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: DataService = TestBed.get(DataService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/app/utils/services/data.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Storage, ref, getStorage, uploadString, listAll, getDownloadURL, deleteObject, StorageReference, ListResult, UploadResult } from '@angular/fire/storage'; 3 | import { getAuth, signInWithEmailAndPassword, createUserWithEmailAndPassword, UserCredential, onAuthStateChanged, User, signOut } from "@angular/fire/auth"; 4 | import { Photo } from '@capacitor/camera'; 5 | import { from, Observable, ReplaySubject } from 'rxjs'; 6 | import { DataStore } from '../shell/data-store'; 7 | import { ImageListingModel } from '../models/image-listing.model'; 8 | import { concatMap, filter, map } from 'rxjs/operators'; 9 | 10 | @Injectable({ 11 | providedIn: 'root' 12 | }) 13 | 14 | export class DataService { 15 | public photos: string[] = []; 16 | 17 | private friendsDataStore: DataStore; 18 | private postsDataStore: DataStore; 19 | 20 | private currentUser: User; 21 | private loggedUser: ReplaySubject = new ReplaySubject(1); 22 | 23 | 24 | constructor( 25 | private _fireStorage: Storage 26 | ) { 27 | 28 | onAuthStateChanged(getAuth(), (user) => { 29 | if (user) { 30 | // User is signed in, see docs for a list of available properties 31 | this.currentUser = user; 32 | this.loggedUser.next(user); 33 | } else { 34 | // User is signed out 35 | this.currentUser = null; 36 | this.loggedUser.next(null); 37 | } 38 | }); 39 | } 40 | 41 | loggedUserObservable() { 42 | return this.loggedUser.asObservable(); 43 | } 44 | 45 | public signInToFirebase(email: string, password: string): Promise { 46 | return signInWithEmailAndPassword(getAuth(), email, password); 47 | } 48 | 49 | public signUpToFirebase(email: string, password: string): Promise { 50 | return createUserWithEmailAndPassword(getAuth(), email, password); 51 | } 52 | 53 | public signOutFromFirebase() { 54 | return signOut(getAuth()); 55 | } 56 | 57 | public getFriendsImagesDataSource(): Observable { 58 | return this.getImages('public-files/friends'); 59 | } 60 | 61 | public getFriendsImagesDataStore(dataSource: Observable): DataStore { 62 | // Check if we already loaded this object 63 | if (!this.friendsDataStore) { 64 | // Initialize the model specifying that it is a shell model 65 | const shellModel: ImageListingModel = new ImageListingModel(); 66 | this.friendsDataStore = new DataStore(shellModel); 67 | this.friendsDataStore.load(dataSource); 68 | } 69 | 70 | return this.friendsDataStore; 71 | } 72 | 73 | public getPostsImagesDataSource(): Observable { 74 | return this.getImages('public-files/posts'); 75 | } 76 | 77 | public getPostsImagesDataStore(dataSource: Observable): DataStore { 78 | // Check if we already loaded this object 79 | if (!this.postsDataStore) { 80 | // Initialize the model specifying that it is a shell model 81 | const shellModel: ImageListingModel = new ImageListingModel(); 82 | this.postsDataStore = new DataStore(shellModel); 83 | this.postsDataStore.load(dataSource); 84 | } 85 | 86 | return this.postsDataStore; 87 | } 88 | 89 | public getPrivateFilesDataSource(): Observable { 90 | return this.loggedUser 91 | .pipe( 92 | filter((user: User) => user != null), 93 | concatMap(user => { 94 | return this.getImages('private-files/' + user.uid); 95 | }) 96 | ) 97 | } 98 | 99 | public getPrivateFilesDataStore(dataSource: Observable): DataStore { 100 | 101 | // Initialize the model specifying that it is a shell model 102 | const shellModel: ImageListingModel = new ImageListingModel(); 103 | 104 | let privateContentDataStore: DataStore = new DataStore(shellModel); 105 | privateContentDataStore.load(dataSource); 106 | 107 | return privateContentDataStore; 108 | } 109 | 110 | // Save picture to file on device 111 | public async savePictureInFirebaseStorage(cameraPhoto: Photo) { 112 | 113 | // Get a reference to the storage service, which is used to create references in your storage bucket 114 | const storageRef = ref(getStorage()); 115 | 116 | // Points to our firestorage folder 'private-files/userID' 117 | const imagesRef = ref(storageRef, 'private-files/' + this.currentUser.uid); 118 | 119 | // Points to 'tutorial-files/file-name.jpeg' 120 | const fileName = new Date().getTime() + '.jpeg'; 121 | const spaceRef = ref(imagesRef, fileName); 122 | 123 | let savedFile: UploadResult = await uploadString(spaceRef, cameraPhoto.base64String, 'base64'); 124 | 125 | return await getDownloadURL(ref(imagesRef, savedFile?.metadata.name)); 126 | } 127 | 128 | // Delete picture 129 | public deletePicture(photo: string, position: number) { 130 | // Remove this photo from the Photos reference data array 131 | this.photos.splice(position, 1); 132 | 133 | // get filename from url path 134 | const filename = photo.split(RegExp('(%2F)..*(%2F)'))[3].split("?")[0]; 135 | 136 | // Get a reference to the storage service, which is used to create references in your storage bucket 137 | const storageRef = ref(getStorage()); 138 | 139 | // Points to our firestorage folder 'private-files/userID' 140 | const imagesRef = ref(storageRef, 'private-files/' + this.currentUser.uid); 141 | 142 | const imageToDeleteRef = ref(imagesRef, filename); 143 | // Delete the file 144 | return deleteObject(imageToDeleteRef); 145 | } 146 | 147 | private getImages(bucketPath: string): Observable { 148 | // Get a reference to the storage service, which is used to create references in your storage bucket 149 | const storage = getStorage(); 150 | 151 | // Create a storage reference from our storage service 152 | const storageRef = ref(storage); 153 | 154 | // Points to our firestorage folder with path bucketPath 155 | const folderRef = ref(storageRef, bucketPath); 156 | 157 | return from(this.getDownloadURLs(folderRef)) 158 | .pipe( 159 | map(urls => { 160 | const model = new ImageListingModel(); 161 | model.imagesUrls = urls; 162 | return model; 163 | }) 164 | ); 165 | } 166 | 167 | private getDownloadURLs(imagesRef: StorageReference): Promise { 168 | return new Promise((resolve, reject) => { 169 | listAll(imagesRef) 170 | .then((listResult: ListResult) => { 171 | let downloadUrlsPromises: Promise[] = []; 172 | 173 | listResult.items.forEach((itemRef: StorageReference) => { 174 | // returns the download url of a given file reference 175 | const itemUrl = getDownloadURL(ref(imagesRef, itemRef.name)); 176 | downloadUrlsPromises.push(itemUrl); 177 | }); 178 | 179 | Promise.all(downloadUrlsPromises) 180 | .then((downloadUrls: string[]) => resolve(downloadUrls)); 181 | }).catch((error) => reject(error)); 182 | }); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/app/utils/shell/aspect-ratio/aspect-ratio.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /src/app/utils/shell/aspect-ratio/aspect-ratio.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | overflow: hidden; 4 | position: relative; 5 | width: 100%; 6 | 7 | .content-wrapper { 8 | position: absolute; 9 | top: 0px; 10 | bottom: 0px; 11 | left: 0px; 12 | right: 0px; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/app/utils/shell/aspect-ratio/aspect-ratio.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, HostBinding } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-aspect-ratio', 5 | templateUrl: './aspect-ratio.component.html', 6 | styleUrls: ['./aspect-ratio.component.scss'] 7 | }) 8 | export class AspectRatioComponent { 9 | 10 | @HostBinding('style.padding') ratioPadding = '0px'; 11 | 12 | @Input() 13 | set ratio(ratio: { w: number, h: number }) { 14 | ratio = (ratio !== undefined && ratio !== null) ? ratio : {w: 1, h: 1}; 15 | 16 | const heightRatio = (ratio.h / ratio.w * 100) + '%'; 17 | 18 | // Conserve aspect ratio (see: http://stackoverflow.com/a/10441480/1116959) 19 | this.ratioPadding = '0px 0px ' + heightRatio + ' 0px'; 20 | } 21 | 22 | constructor() { } 23 | } 24 | -------------------------------------------------------------------------------- /src/app/utils/shell/data-store.ts: -------------------------------------------------------------------------------- 1 | import { Observable, ReplaySubject } from 'rxjs'; 2 | import { map, startWith } from 'rxjs/operators'; 3 | 4 | 5 | export class ShellModel { 6 | isShell = false; 7 | } 8 | 9 | export class DataStore { 10 | // holds a reference to the state emitted by the DataSource. 11 | private timeline: ReplaySubject = new ReplaySubject(1); 12 | 13 | constructor(private shellModel: T) { } 14 | 15 | // Static function with generics 16 | // Append a shell (T & ShellModel) to every value (T) emmited to the timeline 17 | public static AppendShell(dataObservable: Observable, shellModel: T): Observable { 18 | 19 | 20 | // Assign shell flag accordingly 21 | // (ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) 22 | return dataObservable.pipe( 23 | // Dismiss unnecessary delayValue 24 | map((dataValue: T): (T & ShellModel) => Object.assign(dataValue, {isShell: false})), 25 | // Set the shell model as the initial value 26 | startWith(Object.assign(shellModel, {isShell: true})) 27 | ); 28 | } 29 | 30 | load(dataSourceObservable: Observable): void { 31 | DataStore.AppendShell(dataSourceObservable, this.shellModel) 32 | .subscribe((dataValue: T & ShellModel) => { 33 | this.timeline.next(dataValue); 34 | }); 35 | } 36 | 37 | public get state(): Observable { 38 | return this.timeline.asObservable(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/app/utils/shell/image-shell/image-shell.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/app/utils/shell/image-shell/image-shell.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | --image-shell-loading-background: #EEE; 3 | --image-shell-border-radius: 0px; 4 | --image-shell-color: #333; 5 | 6 | display: block; 7 | position: relative; 8 | height: 100%; 9 | border-radius: var(--image-shell-border-radius); 10 | transition: all ease-in-out .3s; 11 | z-index: 2; 12 | 13 | // By default, hide the spinner 14 | & > .spinner { 15 | display: none; 16 | } 17 | 18 | // Loading background 19 | &::before { 20 | content: ''; 21 | background: var(--image-shell-loading-background); 22 | border-radius: var(--image-shell-border-radius); 23 | position: absolute; 24 | top: 0; 25 | bottom: 0; 26 | left: 0; 27 | right: 0; 28 | } 29 | 30 | &:not([display="cover"]) { 31 | width: 100%; 32 | overflow: hidden; 33 | 34 | & > .inner-img { 35 | transition: visibility 0s linear, opacity .5s linear; 36 | opacity: 0; 37 | visibility: hidden; 38 | width: 100%; 39 | height: 100%; 40 | border-radius: var(--image-shell-border-radius); 41 | // Image should fill the space while loading 42 | display: block; 43 | } 44 | 45 | &.img-ssr, 46 | &.img-loaded { 47 | // Hide loading background once the image has loaded 48 | &::before { 49 | display: none; 50 | } 51 | 52 | & > .inner-img { 53 | opacity: 1; 54 | visibility: visible; 55 | } 56 | } 57 | 58 | &.img-error { 59 | & > .inner-img { 60 | // For the Alt text 61 | color: var(--image-shell-color); 62 | font-size: 12px; 63 | } 64 | 65 | // Add placeholder background 66 | &::after { 67 | content: attr(data-error); 68 | display: flex; 69 | align-items: center; 70 | justify-content: center; 71 | position: absolute; 72 | top: 0; 73 | width: 100%; 74 | height: 100%; 75 | box-sizing: border-box; 76 | padding: 10px; 77 | color: var(--image-shell-color); 78 | background-color: var(--image-shell-loading-background); 79 | font-size: 12px; 80 | } 81 | } 82 | } 83 | 84 | // * NOTE: we didn't add .img-error styles for 'cover' display on purpose. 85 | // If it is display: cover 86 | &[display="cover"] { 87 | background-size: cover; 88 | background-repeat: no-repeat; 89 | 90 | // In cover display, we can have content inside the element, thus we need to put these elements beneath 91 | &::before, 92 | & > .spinner { 93 | z-index: -1; 94 | } 95 | 96 | & > .inner-img { 97 | display: none; 98 | visibility: hidden; 99 | } 100 | 101 | &.img-ssr, 102 | &.img-loaded { 103 | // Hide loading background once the image has loaded 104 | &::before { 105 | display: none; 106 | } 107 | } 108 | } 109 | } 110 | 111 | :host([animation="gradient"]) { 112 | --image-shell-loading-background: #EEE; 113 | --image-shell-animation-color: #DDD; 114 | 115 | // The animation that goes beneath the masks 116 | &::before { 117 | background: 118 | linear-gradient(to right, var(--image-shell-loading-background) 8%, var(--image-shell-animation-color) 18%, var(--image-shell-loading-background) 33%); 119 | background-size: 800px 104px; 120 | animation: animateBackground 2s ease-in-out infinite; 121 | } 122 | 123 | &.img-ssr, 124 | &.img-loaded, 125 | &.img-error { 126 | // Reset background animation 127 | &::before { 128 | background: none; 129 | animation: 0; 130 | } 131 | } 132 | 133 | @keyframes animateBackground { 134 | 0%{ 135 | background-position: -468px 0 136 | } 137 | 138 | 100%{ 139 | background-position: 468px 0 140 | } 141 | } 142 | } 143 | 144 | :host([animation="spinner"]) { 145 | --image-shell-spinner-size: 28px; 146 | --image-shell-spinner-color: #CCC; 147 | 148 | & > .spinner { 149 | display: block; 150 | position: absolute; 151 | top: calc(50% - calc(var(--image-shell-spinner-size) / 2)); 152 | left: calc(50% - calc(var(--image-shell-spinner-size) / 2)); 153 | width: var(--image-shell-spinner-size); 154 | height: var(--image-shell-spinner-size); 155 | font-size: var(--image-shell-spinner-size); 156 | line-height: var(--image-shell-spinner-size); 157 | color: var(--image-shell-spinner-color); 158 | } 159 | 160 | &.img-ssr, 161 | &.img-loaded, 162 | &.img-error { 163 | & > .spinner { 164 | display: none; 165 | visibility: hidden; 166 | } 167 | } 168 | } 169 | 170 | :host(.add-overlay) { 171 | --image-shell-overlay-background: rgba(0, 0, 0, .4); 172 | 173 | &.img-ssr, 174 | &.img-loaded, 175 | &.img-error { 176 | // Add background overlay after the image has loaded 177 | &::before { 178 | display: block; 179 | background: var(--image-shell-overlay-background); 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/app/utils/shell/image-shell/image-shell.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, HostBinding } from '@angular/core'; 2 | import { environment } from 'src/environments/environment'; 3 | 4 | @Component({ 5 | selector: 'app-image-shell', 6 | templateUrl: './image-shell.component.html', 7 | styleUrls: ['./image-shell.component.scss'] 8 | }) 9 | export class ImageShellComponent { 10 | // To debug shell styles, change configuration in the environment file 11 | private debugDisplay = (environment.appShellConfig && environment.appShellConfig.debug) ? environment.appShellConfig.debug : false; 12 | 13 | // tslint:disable-next-line:variable-name 14 | _src = ''; 15 | // tslint:disable-next-line:variable-name 16 | _alt = ''; 17 | // tslint:disable-next-line:variable-name 18 | _loadingStrategy: 'lazy' | 'eager' = 'lazy'; 19 | // tslint:disable-next-line:variable-name 20 | _display = ''; 21 | 22 | @HostBinding('class.img-loaded') imageLoaded = false; 23 | @HostBinding('class.img-error') imageError = false; 24 | 25 | @HostBinding('attr.data-error') errorMessage = 'Could not load image'; 26 | 27 | @HostBinding('style.backgroundImage') backgroundImage: string; 28 | 29 | @HostBinding('attr.display') 30 | @Input() 31 | set display(val: string) { 32 | this._display = (val !== undefined && val !== null) ? val : ''; 33 | 34 | // For display 'cover' we use a hidden aux image. As it's hidden, if set loading to 'lazy' it won't ever trigger the loading mechanism 35 | if (this._display === 'cover') { 36 | this._loadingStrategy = 'eager'; 37 | } 38 | } 39 | get display(): string { 40 | return this._display; 41 | } 42 | 43 | @Input() 44 | set src(val: string) { 45 | if (!this.debugDisplay) { 46 | this._src = (val !== undefined && val !== null) ? val : ''; 47 | } 48 | 49 | if (this._display === 'cover') { 50 | // Unset the background-image until the image is loaded 51 | this.backgroundImage = 'unset'; 52 | } 53 | } 54 | 55 | @Input() 56 | set alt(val: string) { 57 | this._alt = (val !== undefined && val !== null) ? val : ''; 58 | } 59 | 60 | constructor() { } 61 | 62 | _imageLoaded(): void { 63 | this.imageLoaded = true; 64 | 65 | // If it's a cover image then set the background-image property accordingly 66 | if (this._display === 'cover') { 67 | // Now that the image is loaded, set the background image 68 | this.backgroundImage = 'url(' + this._src + ')'; 69 | } 70 | } 71 | 72 | _imageLoadError(event: Event): void { 73 | // Image error event get's called when the src is empty. We use emty values for the shell. 74 | // (see: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#Image_loading_errors) 75 | // Avoid that shell case 76 | if (this._src && this._src !== '') { 77 | this.imageLoaded = false; 78 | 79 | setTimeout(() => { 80 | this.imageError = true; 81 | }, 500); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/app/utils/shell/shell.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { IonicModule } from '@ionic/angular'; 4 | 5 | import { AspectRatioComponent } from './aspect-ratio/aspect-ratio.component'; 6 | import { ImageShellComponent } from './image-shell/image-shell.component'; 7 | 8 | @NgModule({ 9 | declarations: [ 10 | AspectRatioComponent, 11 | ImageShellComponent 12 | ], 13 | providers: [ 14 | ], 15 | imports: [ 16 | CommonModule, 17 | IonicModule 18 | ], 19 | exports: [ 20 | AspectRatioComponent, 21 | ImageShellComponent 22 | ] 23 | }) 24 | export class ShellModule { } 25 | -------------------------------------------------------------------------------- /src/assets/icon/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionicthemes/ionic-firebase-image-upload/ed5d3866bf6f225d8a6a7e5cce81e5cbce7b1039/src/assets/icon/favicon.png -------------------------------------------------------------------------------- /src/assets/shapes.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | firebase: { 4 | apiKey: "AIzaSyCH5i2BjvyXkIpF-NsyYDZK3TVx-ifvoQA", 5 | authDomain: "ionic-contacts-app.firebaseapp.com", 6 | projectId: "ionic-contacts-app", 7 | storageBucket: "ionic-contacts-app.appspot.com", 8 | messagingSenderId: "38298401556", 9 | appId: "1:38298401556:web:3a7d8eba7b11c5aa74a001" 10 | }, 11 | appShellConfig: { 12 | debug: false 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | firebase: { 8 | apiKey: "AIzaSyCH5i2BjvyXkIpF-NsyYDZK3TVx-ifvoQA", 9 | authDomain: "ionic-contacts-app.firebaseapp.com", 10 | projectId: "ionic-contacts-app", 11 | storageBucket: "ionic-contacts-app.appspot.com", 12 | messagingSenderId: "38298401556", 13 | appId: "1:38298401556:web:3a7d8eba7b11c5aa74a001" 14 | }, 15 | appShellConfig: { 16 | debug: false 17 | } 18 | }; 19 | 20 | /* 21 | * For easier debugging in development mode, you can import the following file 22 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 23 | * 24 | * This import should be commented out in production mode because it will have a negative impact 25 | * on performance if an error is thrown. 26 | */ 27 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 28 | -------------------------------------------------------------------------------- /src/global.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * App Global CSS 3 | * ---------------------------------------------------------------------------- 4 | * Put style rules here that you want to apply globally. These styles are for 5 | * the entire app and not just one component. Additionally, this file can be 6 | * used as an entry point to import other CSS/Sass files to be included in the 7 | * output CSS. 8 | * For more information on global stylesheets, visit the documentation: 9 | * https://ionicframework.com/docs/layout/global-stylesheets 10 | */ 11 | 12 | /* Core CSS required for Ionic components to work properly */ 13 | @import "~@ionic/angular/css/core.css"; 14 | 15 | /* Basic CSS for apps built with Ionic */ 16 | @import "~@ionic/angular/css/normalize.css"; 17 | @import "~@ionic/angular/css/structure.css"; 18 | @import "~@ionic/angular/css/typography.css"; 19 | @import '~@ionic/angular/css/display.css'; 20 | 21 | /* Optional CSS utils that can be commented out */ 22 | @import "~@ionic/angular/css/padding.css"; 23 | @import "~@ionic/angular/css/float-elements.css"; 24 | @import "~@ionic/angular/css/text-alignment.css"; 25 | @import "~@ionic/angular/css/text-transformation.css"; 26 | @import "~@ionic/angular/css/flex-utils.css"; 27 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Ionic App 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | import { defineCustomElements } from '@ionic/pwa-elements/loader'; 8 | 9 | if (environment.production) { 10 | enableProdMode(); 11 | } 12 | 13 | platformBrowserDynamic() 14 | .bootstrapModule(AppModule) 15 | .catch(err => console.log(err)); 16 | 17 | // Call the element loader after the platform has been bootstrapped 18 | defineCustomElements(window); 19 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | import './zone-flags'; 56 | 57 | /*************************************************************************************************** 58 | * Zone JS is required by default for Angular itself. 59 | */ 60 | import 'zone.js/dist/zone'; // Included with Angular CLI. 61 | 62 | 63 | /*************************************************************************************************** 64 | * APPLICATION IMPORTS 65 | */ 66 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: { 11 | context(path: string, deep?: boolean, filter?: RegExp): { 12 | keys(): string[]; 13 | (id: string): T; 14 | }; 15 | }; 16 | 17 | // First, initialize the Angular testing environment. 18 | getTestBed().initTestEnvironment( 19 | BrowserDynamicTestingModule, 20 | platformBrowserDynamicTesting() 21 | ); 22 | // Then we find all the tests. 23 | const context = require.context('./', true, /\.spec\.ts$/); 24 | // And load the modules. 25 | context.keys().map(context); 26 | -------------------------------------------------------------------------------- /src/theme/variables.scss: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /src/zone-flags.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Prevents Angular change detection from 3 | * running with certain Web Component callbacks 4 | */ 5 | // eslint-disable-next-line no-underscore-dangle 6 | (window as any).__Zone_disable_customElements = true; 7 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts", 10 | "src/polyfills.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "sourceMap": true, 8 | "declaration": false, 9 | "downlevelIteration": true, 10 | "experimentalDecorators": true, 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "module": "es2020", 15 | "lib": ["es2018", "dom"] 16 | }, 17 | "angularCompilerOptions": { 18 | "enableI18nLegacyMessageIdFormat": false, 19 | "strictInjectionParameters": true, 20 | "strictInputAccessModifiers": true, 21 | "strictTemplates": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | --------------------------------------------------------------------------------