Transfer repository from Gitlab
46
mobile/.gitignore
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
**/doc/api/
|
||||||
|
**/ios/Flutter/.last_build_id
|
||||||
|
.dart_tool/
|
||||||
|
.flutter-plugins
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
.packages
|
||||||
|
.pub-cache/
|
||||||
|
.pub/
|
||||||
|
/build/
|
||||||
|
|
||||||
|
# Web related
|
||||||
|
lib/generated_plugin_registrant.dart
|
||||||
|
|
||||||
|
# Symbolication related
|
||||||
|
app.*.symbols
|
||||||
|
|
||||||
|
# Obfuscation related
|
||||||
|
app.*.map.json
|
||||||
|
|
||||||
|
# Android Studio will place build artifacts here
|
||||||
|
/android/app/debug
|
||||||
|
/android/app/profile
|
||||||
|
/android/app/release
|
10
mobile/.metadata
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# This file tracks properties of this Flutter project.
|
||||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
|
#
|
||||||
|
# This file should be version controlled and should not be manually edited.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: 77d935af4db863f6abd0b9c31c7e6df2a13de57b
|
||||||
|
channel: stable
|
||||||
|
|
||||||
|
project_type: app
|
16
mobile/README.md
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# immich_mobile
|
||||||
|
|
||||||
|
A new Flutter project.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
This project is a starting point for a Flutter application.
|
||||||
|
|
||||||
|
Few resources to get you started if this is your first Flutter project:
|
||||||
|
|
||||||
|
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
|
||||||
|
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
|
||||||
|
|
||||||
|
For help getting started with Flutter, view our
|
||||||
|
[online documentation](https://flutter.dev/docs), which offers tutorials,
|
||||||
|
samples, guidance on mobile development, and a full API reference.
|
29
mobile/analysis_options.yaml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# This file configures the analyzer, which statically analyzes Dart code to
|
||||||
|
# check for errors, warnings, and lints.
|
||||||
|
#
|
||||||
|
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||||
|
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||||
|
# invoked from the command line by running `flutter analyze`.
|
||||||
|
|
||||||
|
# The following line activates a set of recommended lints for Flutter apps,
|
||||||
|
# packages, and plugins designed to encourage good coding practices.
|
||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
linter:
|
||||||
|
# The lint rules applied to this project can be customized in the
|
||||||
|
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||||
|
# included above or to enable additional rules. A list of all available lints
|
||||||
|
# and their documentation is published at
|
||||||
|
# https://dart-lang.github.io/linter/lints/index.html.
|
||||||
|
#
|
||||||
|
# Instead of disabling a lint rule for the entire project in the
|
||||||
|
# section below, it can also be suppressed for a single line of code
|
||||||
|
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||||
|
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||||
|
# producing the lint.
|
||||||
|
rules:
|
||||||
|
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||||
|
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
|
|
||||||
|
# Additional information about this file can be found at
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
13
mobile/android/.gitignore
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
gradle-wrapper.jar
|
||||||
|
/.gradle
|
||||||
|
/captures/
|
||||||
|
/gradlew
|
||||||
|
/gradlew.bat
|
||||||
|
/local.properties
|
||||||
|
GeneratedPluginRegistrant.java
|
||||||
|
|
||||||
|
# Remember to never publicly share your keystore.
|
||||||
|
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
|
||||||
|
key.properties
|
||||||
|
**/*.keystore
|
||||||
|
**/*.jks
|
68
mobile/android/app/build.gradle
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
def localProperties = new Properties()
|
||||||
|
def localPropertiesFile = rootProject.file('local.properties')
|
||||||
|
if (localPropertiesFile.exists()) {
|
||||||
|
localPropertiesFile.withReader('UTF-8') { reader ->
|
||||||
|
localProperties.load(reader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||||
|
if (flutterRoot == null) {
|
||||||
|
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||||
|
if (flutterVersionCode == null) {
|
||||||
|
flutterVersionCode = '1'
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||||
|
if (flutterVersionName == null) {
|
||||||
|
flutterVersionName = '1.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.android.application'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion flutter.compileSdkVersion
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = '1.8'
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main.java.srcDirs += 'src/main/kotlin'
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
|
applicationId "com.example.immich_mobile"
|
||||||
|
minSdkVersion flutter.minSdkVersion
|
||||||
|
targetSdkVersion flutter.targetSdkVersion
|
||||||
|
versionCode flutterVersionCode.toInteger()
|
||||||
|
versionName flutterVersionName
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
// TODO: Add your own signing config for the release build.
|
||||||
|
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||||
|
signingConfig signingConfigs.debug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flutter {
|
||||||
|
source '../..'
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
|
}
|
7
mobile/android/app/src/debug/AndroidManifest.xml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.example.immich_mobile">
|
||||||
|
<!-- Flutter needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
38
mobile/android/app/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.example.immich_mobile">
|
||||||
|
<application
|
||||||
|
android:label="immich_mobile"
|
||||||
|
android:name="${applicationName}"
|
||||||
|
android:icon="@mipmap/ic_launcher">
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:theme="@style/LaunchTheme"
|
||||||
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
android:windowSoftInputMode="adjustResize">
|
||||||
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
|
the Android process has started. This theme is visible to the user
|
||||||
|
while the Flutter UI initializes. After that, this theme continues
|
||||||
|
to determine the Window background behind the Flutter UI. -->
|
||||||
|
<meta-data
|
||||||
|
android:name="io.flutter.embedding.android.NormalTheme"
|
||||||
|
android:resource="@style/NormalTheme"
|
||||||
|
/>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
</activity>
|
||||||
|
<!-- Don't delete the meta-data below.
|
||||||
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
|
<meta-data
|
||||||
|
android:name="flutterEmbedding"
|
||||||
|
android:value="2"/>
|
||||||
|
|
||||||
|
|
||||||
|
</application>
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.example.immich_mobile
|
||||||
|
|
||||||
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
|
||||||
|
class MainActivity: FlutterActivity() {
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Modify this file to customize your launch splash screen -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="?android:colorBackground" />
|
||||||
|
|
||||||
|
<!-- You can insert your own image assets here -->
|
||||||
|
<!-- <item>
|
||||||
|
<bitmap
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@mipmap/launch_image" />
|
||||||
|
</item> -->
|
||||||
|
</layer-list>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Modify this file to customize your launch splash screen -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@android:color/white" />
|
||||||
|
|
||||||
|
<!-- You can insert your own image assets here -->
|
||||||
|
<!-- <item>
|
||||||
|
<bitmap
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@mipmap/launch_image" />
|
||||||
|
</item> -->
|
||||||
|
</layer-list>
|
BIN
mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 544 B |
BIN
mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 442 B |
BIN
mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 721 B |
BIN
mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
18
mobile/android/app/src/main/res/values-night/styles.xml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
|
Flutter draws its first frame -->
|
||||||
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
|
</style>
|
||||||
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
This theme determines the color of the Android Window while your
|
||||||
|
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||||
|
running.
|
||||||
|
|
||||||
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
18
mobile/android/app/src/main/res/values/styles.xml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
|
Flutter draws its first frame -->
|
||||||
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
|
</style>
|
||||||
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
This theme determines the color of the Android Window while your
|
||||||
|
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||||
|
running.
|
||||||
|
|
||||||
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
7
mobile/android/app/src/profile/AndroidManifest.xml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.example.immich_mobile">
|
||||||
|
<!-- Flutter needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
31
mobile/android/build.gradle
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
buildscript {
|
||||||
|
ext.kotlin_version = '1.6.10'
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.buildDir = '../build'
|
||||||
|
subprojects {
|
||||||
|
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||||
|
}
|
||||||
|
subprojects {
|
||||||
|
project.evaluationDependsOn(':app')
|
||||||
|
}
|
||||||
|
|
||||||
|
task clean(type: Delete) {
|
||||||
|
delete rootProject.buildDir
|
||||||
|
}
|
3
mobile/android/gradle.properties
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
org.gradle.jvmargs=-Xmx1536M
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.enableJetifier=true
|
6
mobile/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#Fri Jun 23 08:50:38 CEST 2017
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
|
11
mobile/android/settings.gradle
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
include ':app'
|
||||||
|
|
||||||
|
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
|
||||||
|
def properties = new Properties()
|
||||||
|
|
||||||
|
assert localPropertiesFile.exists()
|
||||||
|
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
|
||||||
|
|
||||||
|
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||||
|
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||||
|
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
|
BIN
mobile/assets/immich-logo-no-outline.png
Normal file
After Width: | Height: | Size: 144 KiB |
BIN
mobile/assets/immich-logo.png
Normal file
After Width: | Height: | Size: 280 KiB |
98
mobile/assets/immich-logo.svg
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 26.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="svg2781" xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 564.2 553.5"
|
||||||
|
style="enable-background:new 0 0 564.2 553.5;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#4081EF;stroke:#512D8C;stroke-miterlimit:10;}
|
||||||
|
.st1{fill:#31A452;stroke:#512D8C;stroke-miterlimit:10;}
|
||||||
|
.st2{fill:#DE7FB3;stroke:#512D8C;stroke-miterlimit:10;}
|
||||||
|
.st3{fill:#FFB800;stroke:#512D8C;stroke-miterlimit:10;}
|
||||||
|
.st4{fill:#E64132;stroke:#512D8C;stroke-miterlimit:10;}
|
||||||
|
.st5{fill:#F2F5FB;stroke:#512D8C;stroke-miterlimit:10;}
|
||||||
|
</style>
|
||||||
|
<path class="st0" d="M210.5,549.6c-2.2-0.2-5.5-1-9.7-2.2c-52.4-15.7-99-46.5-133.8-88.5c-8.8-10.7-17.2-22.4-19.4-27.5
|
||||||
|
c-8.1-18.1-6.3-38.7,4.8-55.4c5-7.5,13.2-15,20.5-18.7c1.2-0.6,54.1-20,55.8-20.4c0.5-0.1,0.5,0.2-0.3,2.1c-0.7,1.7-1,3.1-1.1,5.5
|
||||||
|
l-0.1,3.2l2.8,5.8c8.7,17.9,19.2,32.7,33.2,46.4c6.3,6.2,7.8,7.6,13.8,12.3c22.7,18.1,52,30.7,79.9,34.3c2.5,0.3,5,0.8,5.7,1
|
||||||
|
c2.8,0.9,7.7-0.8,11-3.7l1.8-1.6l-0.2,4.8c-0.1,2.7-0.6,15.4-1,28.3c-0.6,20.3-0.8,24-1.5,27.5c-3.9,20.7-18.6,37.5-38.4,44.1
|
||||||
|
c-4.6,1.5-8,2.2-13.1,2.7C216.6,550.1,215.3,550,210.5,549.6z"/>
|
||||||
|
<path class="st1" d="M339.8,549.4c-4-0.4-9.4-1.6-13.2-2.9c-3.4-1.2-10-4.4-12.5-6.1c-10.9-7.4-19-17.9-23.1-30
|
||||||
|
c-2.2-6.7-2.3-7.5-3.3-36.9c-0.5-14.9-0.9-27.9-0.9-28.9l0-1.9l2.3,1.8c2.6,2,6.6,3.4,8.5,3.1c0.6-0.1,3-0.5,5.3-0.8
|
||||||
|
c37.7-5.3,71.2-22.2,97.4-49.1c12.2-12.5,21.4-25.5,29.9-42.4l3.5-7l0-3.6c0-3.1-0.1-3.8-1-5.7c-0.5-1.2-0.9-2.1-0.9-2.2
|
||||||
|
c0.2-0.2,55.3,20.1,56.9,20.9c2.6,1.3,6.6,4.1,9.9,7c9.2,7.7,16.1,19.4,18.8,31.8c0.7,3.1,0.8,4.8,0.8,11.3c0,8.6-0.5,11.7-2.9,18.7
|
||||||
|
c-1.7,5-2.9,7.2-7.1,13.1c-7.6,11-15.3,20.5-25.2,31.2c-32.8,35.4-76.5,62.5-123.4,76.3C351.6,549.6,347.2,550.1,339.8,549.4z"/>
|
||||||
|
<path class="st2" d="M255.6,438c-25.9-4.2-50.7-14.9-71.7-31c-5.2-4-8.7-7.1-14.1-12.4c-12.7-12.5-21.9-24.9-30.5-41.4
|
||||||
|
c-2.3-4.4-2.4-4.7-2.4-7.1c0-8.8,8.5-15.2,16.9-12.7c5.6,1.7,9.6,6.8,9.7,12.2c0,2.6-0.8,4.6-2.6,6.2c-1.2,1.1-3.2,1.9-4.6,1.9
|
||||||
|
c-1.2,0-3.3-0.8-4.3-1.6c-2.1-1.8-2-1,0.4,3.2c19.3,33.8,52.3,59.1,90,69.1c5.7,1.5,11.5,2.7,11.8,2.4c0.1-0.1-0.4-0.8-1.3-1.6
|
||||||
|
c-5.1-4.5-2.3-11.7,5-12.8c5.4-0.8,11.4,2.7,13.9,8c0.8,1.7,1,2.5,1,5.3s-0.1,3.5-1,5.3c-2,4.3-6.8,7.9-10.3,7.8
|
||||||
|
C260.6,438.7,257.9,438.3,255.6,438z"/>
|
||||||
|
<path class="st0" d="M297.6,438.2c-3.4-1.3-6.4-4.3-7.8-8.1c-1.1-2.9-0.9-7.3,0.5-10.2c2.6-5.3,8.7-8.5,14.4-7.5
|
||||||
|
c2.9,0.5,4.7,1.9,6,4.3c0.8,1.6,1,2.2,0.8,3.6c-0.3,2.2-0.9,3.3-2.7,4.8c-0.8,0.7-1.4,1.4-1.3,1.5c0.5,0.5,13.4-2.7,21.3-5.4
|
||||||
|
c33.6-11.3,62.5-35.1,80.4-66.1c2.5-4.4,2.6-5,0.5-3.2c-2.8,2.4-7,1.9-9.6-1c-4-4.6-0.7-13.8,6.1-16.9c2-0.9,2.7-1,5.5-1
|
||||||
|
c2.9,0,3.5,0.1,5.6,1.1c4.4,2.1,7.4,6.4,7.8,11c0.2,2.2,0.1,2.3-2.2,6.9c-23,45.9-67,78.1-117.2,85.9
|
||||||
|
C300.2,438.8,299.4,438.9,297.6,438.2z"/>
|
||||||
|
<path class="st1" d="M211.1,398.5c-4.7-0.9-8.7-2.7-12.9-5.9c-10.8-8.1-13.5-22.3-6.6-33.7c0.7-1.2,1.1-2.2,1-2.4
|
||||||
|
c-0.2-0.2-1.2-0.6-2.3-1.1c-7.6-3-13-10.6-13.5-19.1c-0.5-7.4,3.1-15,9-19.4c1-0.7,2.2-1.5,2.6-1.8c0.8-0.4,68.9-22.7,69.4-22.7
|
||||||
|
c0.2,0,0.7,0.7,1.2,1.5c0.5,0.8,1.6,2.3,2.4,3.3c1.2,1.4,1.5,1.9,1.2,2.3c-0.2,0.3-6.9,9.5-14.8,20.5
|
||||||
|
c-15.9,21.9-15.5,21.3-13.4,23.4c1.3,1.3,2.9,1.4,4.4,0.3c0.6-0.4,7.5-9.7,15.5-20.7c11.2-15.4,14.6-19.9,15-19.7
|
||||||
|
c0.9,0.4,5.5,1.9,6.6,2.1l1,0.2l0,35.3c0,39.7,0,38.8-2.5,44c-2.6,5.3-7.2,9.3-12.7,11.2c-3.7,1.3-6.8,1.6-10.2,1
|
||||||
|
c-5.5-0.9-9.8-3.2-13.7-7.4l-2.2-2.4l-0.6,0.9c-3,4.3-8.6,8.1-14,9.5C218.2,398.6,213.2,398.9,211.1,398.5z"/>
|
||||||
|
<path class="st3" d="M342.9,398.5c-5.5-0.9-9.9-3.2-14.3-7.6l-3.2-3.2l-0.7,1c-2.3,3.3-6.8,6.5-11.1,7.9c-3.7,1.2-9.2,1.4-12.6,0.3
|
||||||
|
c-7.1-2.1-12.7-7.4-15.2-14.3l-0.9-2.6v-37.1v-37.1l1.8-0.4c1-0.2,2.7-0.8,3.9-1.2c1.1-0.5,2.1-0.8,2.2-0.7c0.1,0.1,6.5,9,14.4,19.9
|
||||||
|
c7.8,10.9,14.7,20.1,15.2,20.5c2.2,1.9,5.4,0.4,5.4-2.6c0-1.4-1-2.9-13.8-20.5c-7.6-10.5-14.2-19.6-14.7-20.4l-0.9-1.3l1.4-1.7
|
||||||
|
c0.8-0.9,1.9-2.5,2.5-3.4l1-1.6l34.4,11.2c18.9,6.2,35.1,11.6,35.9,12.1c6.8,4,11.1,11.3,11.1,19.1c0,4.1-0.5,6.4-2.4,10.2
|
||||||
|
c-2,4.1-5.5,7.6-9.6,9.7c-1.6,0.8-3.2,1.5-3.4,1.5c-1,0-0.9,0.7,0.3,2.6c2.8,4.3,4,8.5,3.9,13.7c0,8.1-3.7,15.2-10.6,20.3
|
||||||
|
C356.4,397.6,349.5,399.5,342.9,398.5z"/>
|
||||||
|
<path class="st2" d="M53.9,341.9c-0.5-0.1-2.3-0.4-3.9-0.7c-15.6-2.6-30.4-12.6-38.8-26.2c-3.5-5.7-6.4-13.2-7.8-19.9
|
||||||
|
c-1.2-6.1-0.8-28.1,0.8-43.1c4.5-43,19-84.3,42.2-120.7c6.5-10.2,14.9-21.5,18.2-24.6c17.8-16.6,43.1-20.5,64.8-10
|
||||||
|
c4.3,2.1,8.8,5.1,12.7,8.6c2.8,2.4,5.8,6.1,20.9,25.5c9.7,12.5,17.8,22.8,17.9,23c0.2,0.2-0.9,0.4-3.2,0.4c-2.5,0-4.1,0.2-5.7,0.7
|
||||||
|
c-2.1,0.7-2.6,1.1-7.9,6.3c-8.2,8.1-14.4,15.3-20.3,23.9c-15.5,22.2-25.4,47.7-28.8,74.8c-2.2,16.9-1.6,37.5,1.6,52.3
|
||||||
|
c0.3,1.4,0.5,2.8,0.4,3c-0.1,0.2,0.2,1.3,0.8,2.4c1.1,2.4,4.3,5.7,6.5,6.8l1.5,0.8l-1.2,0.4c-0.7,0.2-13.1,3.8-27.6,8
|
||||||
|
c-16.4,4.7-27.7,7.8-29.8,8.1C64.1,342.1,56.1,342.3,53.9,341.9z"/>
|
||||||
|
<path class="st3" d="M494.7,341.7c-2.1-0.3-33.8-9.1-56.5-15.8l-2.5-0.7l1.6-0.8c3.4-1.7,7.2-6.6,7.3-9.6c0-0.7,0.4-3.3,0.8-5.8
|
||||||
|
c3.9-22.7,3.1-46.1-2.5-68.4c-6.4-25.5-18.6-49.2-35.8-69.1c-4.6-5.3-14.8-15.4-16.4-16.1c-2.4-1.1-5.1-1.6-8-1.4l-2.7,0.2l1.2-1.5
|
||||||
|
c0.7-0.8,8.5-10.8,17.5-22.3c8.9-11.5,17.2-21.8,18.5-23.1c2.6-2.7,7-6.2,10.3-8.2c19.3-11.6,43-11.1,61.6,1.2
|
||||||
|
c5.4,3.6,8.2,6.2,12.3,11.7c26.4,34.5,44,73.7,52.3,116.2c3.4,17.6,4.9,33.3,5,52.4c0,13-0.2,14.8-2.5,21.8
|
||||||
|
C547.8,328.6,521.7,345.2,494.7,341.7z"/>
|
||||||
|
<path class="st4" d="M133.9,318.5c-2-0.5-4.6-1.9-6-3.3c-2.5-2.4-3.1-3.5-3.7-7.3c-4.4-27.3-2.2-54,6.7-79.3
|
||||||
|
c5.3-15.1,13.5-30.5,23-43.1c5.8-7.8,16.6-19.5,19-20.7c4.7-2.4,11.3-1.2,15.2,2.7c5.4,5.4,5.2,13.9-0.3,19.1
|
||||||
|
c-4.3,4-9.4,4.4-12.6,0.9c-1.7-1.9-2.2-3.9-1.7-6.4c0.2-1.1,0.3-2,0.2-2.2c-0.3-0.3-3.6,3.3-8.3,9.1c-17.6,21.8-28.5,48-31.9,76.5
|
||||||
|
c-1.1,9.3-1,26.4,0.1,34.6c0.3,1.8,0.8,1.9,1.4,0.1c0.9-2.6,4-4.7,6.8-4.7c3,0,5.9,2.2,7.5,5.7c0.6,1.3,0.8,2.3,0.8,5.2
|
||||||
|
c0,3.3-0.1,3.8-1.1,5.7c-1.4,2.7-4.6,5.7-7.1,6.6C139.4,318.6,135.8,318.9,133.9,318.5z"/>
|
||||||
|
<path class="st1" d="M422.6,318.5c-3.7-0.6-7.7-3.6-9.4-7.1c-3.8-7.5,0.1-16.9,6.9-16.9c3.1,0,5.8,2,6.9,5.2
|
||||||
|
c0.4,1.2,0.5,1.3,0.7,0.7c1.3-3.7,1.7-26.4,0.6-35.7c-3.6-29.6-14.5-55.3-33-77.9c-5.5-6.7-8.4-9.4-7.1-6.6c0.7,1.4,0.5,4.3-0.3,5.9
|
||||||
|
c-0.9,1.7-3.2,3.5-5,3.8c-3.2,0.6-7.9-1.6-10.2-4.8c-6.5-8.8-0.5-21.2,10.4-21.4c4.6-0.1,5.2,0.3,11.2,6.4
|
||||||
|
c12.1,12.3,21.1,24.9,28.8,40.3c13.2,26.3,18.6,54.9,16.1,84.5c-0.5,5.6-2,15.7-2.6,17.1c-1.3,2.8-4.8,5.5-8.4,6.5
|
||||||
|
C425.9,318.9,425.1,318.9,422.6,318.5z"/>
|
||||||
|
<path class="st0" d="M178.2,307.2c-6-1.3-12.2-6.2-14.9-11.7c-3.4-7-3.1-15.1,0.9-21.6c0.7-1.2,1.2-2.3,1.1-2.4
|
||||||
|
c-0.1-0.1-1.1-0.6-2.1-1c-3.9-1.5-8.1-4.8-10.7-8.3c-4.6-6.2-6.1-14.6-3.9-22.1c2.9-10.3,9.4-16.8,19.1-19.3c2.8-0.7,9-0.8,11.7,0
|
||||||
|
c1.1,0.3,2.2,0.5,2.4,0.5c0.2,0,0.3-0.7,0.3-1.5c0-2.9,0.8-5.8,2.4-9.2c5.2-10.8,18.1-15.5,29-10.5c2.7,1.2,6.2,3.8,7.8,5.8
|
||||||
|
c0.7,0.8,10.3,14,21.5,29.4l20.3,27.9l-1.5,1.8c-0.8,1-1.9,2.6-2.5,3.5c-0.6,1-1.2,1.7-1.5,1.6c-4.5-1.7-46.7-15-47.7-15
|
||||||
|
c-1.9,0-3.1,1.3-3.1,3.2c0,1,0.2,1.7,0.8,2.3c0.6,0.6,7.8,3.1,24.5,8.5l23.7,7.7l-0.1,4.3l-0.1,4.3L223,295.9
|
||||||
|
c-18,5.9-33.9,10.9-35.2,11.2C184.7,307.8,181.2,307.8,178.2,307.2z"/>
|
||||||
|
<path class="st4" d="M372.5,306.8c-1.8-0.5-17.5-5.6-35-11.3l-31.8-10.4l1-4.3v-4.3l22.6-7.7c15-4.9,24-8,24.6-8.5
|
||||||
|
c0.7-0.6,0.9-1.1,0.9-2.2c0-2-1.2-3.3-3.1-3.3c-0.9,0-10.5,2.9-24.7,7.5c-12.8,4.1-23.4,7.5-23.6,7.5c-0.1,0-0.7-0.8-1.3-1.9
|
||||||
|
c-0.6-1-1.6-2.5-2.2-3.2c-0.7-0.7-1.2-1.5-1.2-1.6c0-0.2,9.6-13.5,21.4-29.6c18.9-26,21.6-29.6,23.6-31.1c5.7-4.4,13.1-5.8,19.7-3.9
|
||||||
|
c9,2.7,16.1,11.6,16.1,20.3c0,2.3-0.1,2.3,3.1,1.5c4.7-1.1,11.5-0.5,16,1.5c4.6,2,9,6,11.5,10.2c2.1,3.6,3.9,9.4,4.2,13.2
|
||||||
|
c0.3,5.2-1.1,10.7-4,15.3c-2.6,4.1-7.8,8.3-12.1,9.8c-0.9,0.3-1.7,0.8-1.7,1c0,0.2,0.4,1,0.9,1.7c2.4,3.6,3.6,7.7,3.5,12.7
|
||||||
|
c0,5.8-2.1,10.7-6.4,15.1c-4,4.1-8.9,6.3-14.9,6.5C376.3,307.7,375.3,307.6,372.5,306.8z"/>
|
||||||
|
<path class="st5" d="M276.2,298.9c-6.1-1.6-11.4-6.8-13.2-12.9c-0.7-2.4-0.7-7.5,0-9.9c1.7-5.8,6.6-10.8,12.3-12.5
|
||||||
|
c2.7-0.8,7.2-0.9,10-0.2c6.2,1.6,11.6,7.1,13.2,13.3c1.6,6-0.3,12.6-5,17.3C288.9,298.6,282.2,300.5,276.2,298.9z"/>
|
||||||
|
<path class="st2" d="M248.3,229.8c-13.3-18.3-21.2-29.6-22-31.1c-1.4-3-1.9-5.5-1.9-9.4c0-14.1,13.1-24.4,27.1-21.4
|
||||||
|
c1.4,0.3,2.6,0.5,2.7,0.5s0.3-1.3,0.4-2.8c0.8-10.7,8.4-19.6,18.9-22.4c3.9-1,10.6-1,14.5,0c8.9,2.3,15.9,9.3,18.2,18.2
|
||||||
|
c0.4,1.5,0.7,3.7,0.7,4.9c0,1.2,0.1,2.1,0.3,2.1s1.5-0.3,3-0.6c7.4-1.6,15.2,0.7,20.5,6c4.3,4.3,6.6,9.6,6.6,15.6
|
||||||
|
c0,4-0.6,6.5-2.4,10c-0.6,1.2-10.4,15-21.7,30.7c-17.8,24.5-20.8,28.5-21.4,28.3c-0.4-0.1-1.9-0.6-3.4-1.1c-1.5-0.5-2.9-0.9-3.3-0.9
|
||||||
|
c-0.7,0-0.7-0.8-0.3-25.5v-25.5l-1.4-0.9c-1-1.1-2.5-1.5-3.8-0.9c-2,0.8-2-0.5-1.8,27.2v25.8h-1.2c-0.5-0.2-2.4,0.3-4,0.9
|
||||||
|
s-3.1,1.1-3.2,1.1C269.2,258.5,259.8,245.6,248.3,229.8z"/>
|
||||||
|
<path class="st3" d="M210.9,164.8c-4.1-0.9-7.7-3.6-9.6-7.4c-1.4-2.8-1.7-7.3-0.5-10.3c1.7-4.5,3.9-6.1,15.6-11.2
|
||||||
|
c15.8-7,31.4-11.1,49.2-12.9c7.3-0.8,23.2-0.8,30.6,0c17.4,1.8,33.3,6,49.1,13c7.3,3.2,12.5,6.1,13.6,7.5c4.3,5.6,3.8,12.7-1.1,17.6
|
||||||
|
c-5.1,5.1-12.9,5.4-18.1,0.7c-2-1.8-3-3.5-3.4-5.6c-0.7-4,2.9-8.1,7.3-8.2c1.4,0,1.5-0.1,1.1-0.5c-0.3-0.3-2.2-1.2-4.3-2.1
|
||||||
|
c-33.2-14.5-70.5-16.4-105-5.4c-7.5,2.4-19,7.2-18.6,7.7c0.1,0.2,0.8,0.3,1.6,0.3c5.6,0,9.1,6.2,6.1,10.8
|
||||||
|
C221.6,163.3,215.9,165.9,210.9,164.8z"/>
|
||||||
|
<path class="st4" d="M174.7,123.4c-8.9-13.1-16.8-25.1-17.5-26.6c-1.6-3.3-3.6-9.2-4.4-13c-2.6-12.5-0.9-25.8,5-37.5
|
||||||
|
c4.2-8.3,11.2-16.3,18.6-21.3c5-3.4,6.1-3.9,12.8-6.3c23.1-8.2,47.2-13.1,73.4-15c7.5-0.6,28.5-0.6,36.3,0
|
||||||
|
c25.5,1.8,50.6,6.9,73,14.8c6.4,2.2,8.2,3.1,13.1,6.5c9.8,6.6,18.1,17.5,22,29.2c2.2,6.5,2.7,10,2.7,17.9c0,7.9-0.5,11.3-2.7,17.9
|
||||||
|
c-2.3,6.8-3.7,9.1-20.3,33.6l-16.1,23.8l-0.4-2.2c-0.2-1.2-0.9-3-1.4-4c-1-1.8-4.4-5.6-4.7-5.2c-0.1,0.1-1.2-0.4-2.4-1.1
|
||||||
|
c-9.1-5.2-21.9-10.5-33.2-13.9c-37-11-77.2-8.8-113,6.1c-4.9,2.1-17.7,8.4-19.2,9.5c-2.2,1.6-5.1,6.8-5.1,9c0,0.4-0.1,1-0.3,1.2
|
||||||
|
C191,147,184.7,138,174.7,123.4z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 9.7 KiB |
34
mobile/ios/.gitignore
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
**/dgph
|
||||||
|
*.mode1v3
|
||||||
|
*.mode2v3
|
||||||
|
*.moved-aside
|
||||||
|
*.pbxuser
|
||||||
|
*.perspectivev3
|
||||||
|
**/*sync/
|
||||||
|
.sconsign.dblite
|
||||||
|
.tags*
|
||||||
|
**/.vagrant/
|
||||||
|
**/DerivedData/
|
||||||
|
Icon?
|
||||||
|
**/Pods/
|
||||||
|
**/.symlinks/
|
||||||
|
profile
|
||||||
|
xcuserdata
|
||||||
|
**/.generated/
|
||||||
|
Flutter/App.framework
|
||||||
|
Flutter/Flutter.framework
|
||||||
|
Flutter/Flutter.podspec
|
||||||
|
Flutter/Generated.xcconfig
|
||||||
|
Flutter/ephemeral/
|
||||||
|
Flutter/app.flx
|
||||||
|
Flutter/app.zip
|
||||||
|
Flutter/flutter_assets/
|
||||||
|
Flutter/flutter_export_environment.sh
|
||||||
|
ServiceDefinitions.json
|
||||||
|
Runner/GeneratedPluginRegistrant.*
|
||||||
|
|
||||||
|
# Exceptions to above rules.
|
||||||
|
!default.mode1v3
|
||||||
|
!default.mode2v3
|
||||||
|
!default.pbxuser
|
||||||
|
!default.perspectivev3
|
26
mobile/ios/Flutter/AppFrameworkInfo.plist
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>App</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>io.flutter.flutter.app</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>App</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>MinimumOSVersion</key>
|
||||||
|
<string>9.0</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
2
mobile/ios/Flutter/Debug.xcconfig
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||||
|
#include "Generated.xcconfig"
|
2
mobile/ios/Flutter/Release.xcconfig
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||||
|
#include "Generated.xcconfig"
|
41
mobile/ios/Podfile
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
# Uncomment this line to define a global platform for your project
|
||||||
|
# platform :ios, '9.0'
|
||||||
|
|
||||||
|
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||||
|
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||||
|
|
||||||
|
project 'Runner', {
|
||||||
|
'Debug' => :debug,
|
||||||
|
'Profile' => :release,
|
||||||
|
'Release' => :release,
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutter_root
|
||||||
|
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
|
||||||
|
unless File.exist?(generated_xcode_build_settings_path)
|
||||||
|
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
|
||||||
|
end
|
||||||
|
|
||||||
|
File.foreach(generated_xcode_build_settings_path) do |line|
|
||||||
|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
|
||||||
|
return matches[1].strip if matches
|
||||||
|
end
|
||||||
|
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
|
||||||
|
end
|
||||||
|
|
||||||
|
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
|
||||||
|
|
||||||
|
flutter_ios_podfile_setup
|
||||||
|
|
||||||
|
target 'Runner' do
|
||||||
|
use_frameworks!
|
||||||
|
use_modular_headers!
|
||||||
|
|
||||||
|
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
||||||
|
end
|
||||||
|
|
||||||
|
post_install do |installer|
|
||||||
|
installer.pods_project.targets.each do |target|
|
||||||
|
flutter_additional_ios_build_settings(target)
|
||||||
|
end
|
||||||
|
end
|
50
mobile/ios/Podfile.lock
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
PODS:
|
||||||
|
- device_info_plus (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- Flutter (1.0.0)
|
||||||
|
- FMDB (2.7.5):
|
||||||
|
- FMDB/standard (= 2.7.5)
|
||||||
|
- FMDB/standard (2.7.5)
|
||||||
|
- path_provider_ios (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- photo_manager (1.0.0):
|
||||||
|
- Flutter
|
||||||
|
- FlutterMacOS
|
||||||
|
- sqflite (0.0.2):
|
||||||
|
- Flutter
|
||||||
|
- FMDB (>= 2.7.5)
|
||||||
|
|
||||||
|
DEPENDENCIES:
|
||||||
|
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||||
|
- Flutter (from `Flutter`)
|
||||||
|
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
|
||||||
|
- photo_manager (from `.symlinks/plugins/photo_manager/ios`)
|
||||||
|
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
||||||
|
|
||||||
|
SPEC REPOS:
|
||||||
|
trunk:
|
||||||
|
- FMDB
|
||||||
|
|
||||||
|
EXTERNAL SOURCES:
|
||||||
|
device_info_plus:
|
||||||
|
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||||
|
Flutter:
|
||||||
|
:path: Flutter
|
||||||
|
path_provider_ios:
|
||||||
|
:path: ".symlinks/plugins/path_provider_ios/ios"
|
||||||
|
photo_manager:
|
||||||
|
:path: ".symlinks/plugins/photo_manager/ios"
|
||||||
|
sqflite:
|
||||||
|
:path: ".symlinks/plugins/sqflite/ios"
|
||||||
|
|
||||||
|
SPEC CHECKSUMS:
|
||||||
|
device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed
|
||||||
|
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
|
||||||
|
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||||
|
path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5
|
||||||
|
photo_manager: 84fa94fbeb82e607333ea9a13c43b58e0903a463
|
||||||
|
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
|
||||||
|
|
||||||
|
PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
|
||||||
|
|
||||||
|
COCOAPODS: 1.10.1
|
551
mobile/ios/Runner.xcodeproj/project.pbxproj
Normal file
|
@ -0,0 +1,551 @@
|
||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 51;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||||
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||||
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||||
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||||
|
D218389C4A4C4693F141F7D1 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 886774DBDDE6B35BF2B4F2CD /* Pods_Runner.framework */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 10;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
name = "Embed Frameworks";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||||
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
|
2E3441B73560D0F6FD25E04F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||||
|
886774DBDDE6B35BF2B4F2CD /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||||
|
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||||
|
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||||
|
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
|
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
|
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
E0E99CDC17B3EB7FA8BA2332 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
F7101BB0391A314774615E89 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
D218389C4A4C4693F141F7D1 /* Pods_Runner.framework in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
0FB772A5B9601143383626CA /* Pods */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
2E3441B73560D0F6FD25E04F /* Pods-Runner.debug.xcconfig */,
|
||||||
|
E0E99CDC17B3EB7FA8BA2332 /* Pods-Runner.release.xcconfig */,
|
||||||
|
F7101BB0391A314774615E89 /* Pods-Runner.profile.xcconfig */,
|
||||||
|
);
|
||||||
|
path = Pods;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
1754452DD81DA6620E279E51 /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
886774DBDDE6B35BF2B4F2CD /* Pods_Runner.framework */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
||||||
|
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||||
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||||
|
9740EEB31CF90195004384FC /* Generated.xcconfig */,
|
||||||
|
);
|
||||||
|
name = Flutter;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146E51CF9000F007C117D = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9740EEB11CF90186004384FC /* Flutter */,
|
||||||
|
97C146F01CF9000F007C117D /* Runner */,
|
||||||
|
97C146EF1CF9000F007C117D /* Products */,
|
||||||
|
0FB772A5B9601143383626CA /* Pods */,
|
||||||
|
1754452DD81DA6620E279E51 /* Frameworks */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146EF1CF9000F007C117D /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
97C146EE1CF9000F007C117D /* Runner.app */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146F01CF9000F007C117D /* Runner */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
||||||
|
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||||
|
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||||
|
97C147021CF9000F007C117D /* Info.plist */,
|
||||||
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
||||||
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||||
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||||
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
||||||
|
);
|
||||||
|
path = Runner;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
97C146ED1CF9000F007C117D /* Runner */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||||
|
buildPhases = (
|
||||||
|
4044AF030EF7D8721844FFBA /* [CP] Check Pods Manifest.lock */,
|
||||||
|
9740EEB61CF901F6004384FC /* Run Script */,
|
||||||
|
97C146EA1CF9000F007C117D /* Sources */,
|
||||||
|
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||||
|
97C146EC1CF9000F007C117D /* Resources */,
|
||||||
|
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||||
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
|
D218A34AEE62BC1EF119F5B0 /* [CP] Embed Pods Frameworks */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = Runner;
|
||||||
|
productName = Runner;
|
||||||
|
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
97C146E61CF9000F007C117D /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
LastUpgradeCheck = 1300;
|
||||||
|
ORGANIZATIONNAME = "";
|
||||||
|
TargetAttributes = {
|
||||||
|
97C146ED1CF9000F007C117D = {
|
||||||
|
CreatedOnToolsVersion = 7.3.1;
|
||||||
|
LastSwiftMigration = 1100;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
||||||
|
compatibilityVersion = "Xcode 9.3";
|
||||||
|
developmentRegion = en;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
en,
|
||||||
|
Base,
|
||||||
|
);
|
||||||
|
mainGroup = 97C146E51CF9000F007C117D;
|
||||||
|
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
97C146ED1CF9000F007C117D /* Runner */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
97C146EC1CF9000F007C117D /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
|
||||||
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
||||||
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
||||||
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Thin Binary";
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||||
|
};
|
||||||
|
4044AF030EF7D8721844FFBA /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
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";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Run Script";
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||||
|
};
|
||||||
|
D218A34AEE62BC1EF119F5B0 /* [CP] Embed Pods Frameworks */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Embed Pods Frameworks";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
97C146EA1CF9000F007C117D /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||||
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXVariantGroup section */
|
||||||
|
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
97C146FB1CF9000F007C117D /* Base */,
|
||||||
|
);
|
||||||
|
name = Main.storyboard;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
97C147001CF9000F007C117D /* Base */,
|
||||||
|
);
|
||||||
|
name = LaunchScreen.storyboard;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXVariantGroup section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
249021D3217E4FDB00AE95B9 /* Profile */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Profile;
|
||||||
|
};
|
||||||
|
249021D4217E4FDB00AE95B9 /* Profile */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
DEVELOPMENT_TEAM = C24486LLLU;
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.immichMobile;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
};
|
||||||
|
name = Profile;
|
||||||
|
};
|
||||||
|
97C147031CF9000F007C117D /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"DEBUG=1",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
97C147041CF9000F007C117D /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
97C147061CF9000F007C117D /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
DEVELOPMENT_TEAM = C24486LLLU;
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.immichMobile;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
97C147071CF9000F007C117D /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
DEVELOPMENT_TEAM = C24486LLLU;
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.immichMobile;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
97C147031CF9000F007C117D /* Debug */,
|
||||||
|
97C147041CF9000F007C117D /* Release */,
|
||||||
|
249021D3217E4FDB00AE95B9 /* Profile */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
97C147061CF9000F007C117D /* Debug */,
|
||||||
|
97C147071CF9000F007C117D /* Release */,
|
||||||
|
249021D4217E4FDB00AE95B9 /* Profile */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
};
|
||||||
|
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||||
|
}
|
7
mobile/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>PreviewsEnabled</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,87 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1300"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
|
BuildableName = "Runner.app"
|
||||||
|
BlueprintName = "Runner"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
|
BuildableName = "Runner.app"
|
||||||
|
BlueprintName = "Runner"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
|
BuildableName = "Runner.app"
|
||||||
|
BlueprintName = "Runner"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Profile"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
|
BuildableName = "Runner.app"
|
||||||
|
BlueprintName = "Runner"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
10
mobile/ios/Runner.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "group:Runner.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Pods/Pods.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>PreviewsEnabled</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
13
mobile/ios/Runner/AppDelegate.swift
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import UIKit
|
||||||
|
import Flutter
|
||||||
|
|
||||||
|
@UIApplicationMain
|
||||||
|
@objc class AppDelegate: FlutterAppDelegate {
|
||||||
|
override func application(
|
||||||
|
_ application: UIApplication,
|
||||||
|
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||||
|
) -> Bool {
|
||||||
|
GeneratedPluginRegistrant.register(with: self)
|
||||||
|
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-20x20@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-20x20@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-29x29@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-29x29@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-29x29@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-40x40@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-40x40@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "60x60",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-60x60@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "60x60",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-60x60@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-20x20@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-20x20@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-29x29@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-29x29@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-40x40@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-40x40@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "76x76",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-76x76@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "76x76",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-76x76@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "83.5x83.5",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-83.5x83.5@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "1024x1024",
|
||||||
|
"idiom" : "ios-marketing",
|
||||||
|
"filename" : "Icon-App-1024x1024@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 564 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.5 KiB |
23
mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "LaunchImage.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "LaunchImage@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "LaunchImage@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
BIN
mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
vendored
Normal file
After Width: | Height: | Size: 68 B |
BIN
mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
vendored
Normal file
After Width: | Height: | Size: 68 B |
BIN
mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
vendored
Normal file
After Width: | Height: | Size: 68 B |
5
mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# Launch Screen Assets
|
||||||
|
|
||||||
|
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
|
||||||
|
|
||||||
|
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
|
37
mobile/ios/Runner/Base.lproj/LaunchScreen.storyboard
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--View Controller-->
|
||||||
|
<scene sceneID="EHf-IW-A2E">
|
||||||
|
<objects>
|
||||||
|
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||||
|
<layoutGuides>
|
||||||
|
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
|
||||||
|
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
|
||||||
|
</layoutGuides>
|
||||||
|
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
|
||||||
|
</imageView>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
|
||||||
|
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="53" y="375"/>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
<resources>
|
||||||
|
<image name="LaunchImage" width="168" height="185"/>
|
||||||
|
</resources>
|
||||||
|
</document>
|
26
mobile/ios/Runner/Base.lproj/Main.storyboard
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--Flutter View Controller-->
|
||||||
|
<scene sceneID="tne-QT-ifu">
|
||||||
|
<objects>
|
||||||
|
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
|
||||||
|
<layoutGuides>
|
||||||
|
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
||||||
|
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||||
|
</layoutGuides>
|
||||||
|
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||||
|
</view>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
</document>
|
49
mobile/ios/Runner/Info.plist
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>Immich Mobile</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>immich_mobile</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||||
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
<true />
|
||||||
|
<key>UILaunchStoryboardName</key>
|
||||||
|
<string>LaunchScreen</string>
|
||||||
|
<key>UIMainStoryboardFile</key>
|
||||||
|
<string>Main</string>
|
||||||
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
|
<true />
|
||||||
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
|
<string>App need your agree, can visit your album</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
1
mobile/ios/Runner/Runner-Bridging-Header.h
Normal file
|
@ -0,0 +1 @@
|
||||||
|
#import "GeneratedPluginRegistrant.h"
|
11
mobile/lib/constants/hive_box.dart
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
// Access token
|
||||||
|
const String userInfoBox = "immichBoxUserInfo"; // Box
|
||||||
|
const String accessTokenKey = "immichBoxAccessTokenKey"; // Key 1
|
||||||
|
const String deviceIdKey = 'immichBoxDeviceIdKey'; // Key 2
|
||||||
|
|
||||||
|
// SERVER ENDPOINT
|
||||||
|
const String serverEndpointKey = 'immichBoxServerEndpoint';
|
||||||
|
|
||||||
|
// KEY
|
||||||
|
const String hiveAllAsssetKey = "allAssets";
|
||||||
|
const String hiveBackupProgressKey = "backupProgressAssets";
|
92
mobile/lib/main.dart
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
|
import 'package:immich_mobile/shared/providers/app_state.provider.dart';
|
||||||
|
import 'constants/hive_box.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
await Hive.initFlutter();
|
||||||
|
await Hive.openBox(userInfoBox);
|
||||||
|
// Hive.registerAdapter(ImmichBackUpAssetAdapter());
|
||||||
|
// Hive.deleteBoxFromDisk(hiveImmichBox);
|
||||||
|
|
||||||
|
runApp(const ProviderScope(child: ImmichApp()));
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImmichApp extends ConsumerStatefulWidget {
|
||||||
|
const ImmichApp({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ImmichAppState createState() => _ImmichAppState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserver {
|
||||||
|
@override
|
||||||
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
|
switch (state) {
|
||||||
|
case AppLifecycleState.resumed:
|
||||||
|
debugPrint("[APP STATE] resumed");
|
||||||
|
ref.read(appStateProvider.notifier).state = AppStateEnum.resumed;
|
||||||
|
break;
|
||||||
|
case AppLifecycleState.inactive:
|
||||||
|
debugPrint("[APP STATE] inactive");
|
||||||
|
ref.read(appStateProvider.notifier).state = AppStateEnum.inactive;
|
||||||
|
break;
|
||||||
|
case AppLifecycleState.paused:
|
||||||
|
debugPrint("[APP STATE] paused");
|
||||||
|
ref.read(appStateProvider.notifier).state = AppStateEnum.paused;
|
||||||
|
break;
|
||||||
|
case AppLifecycleState.detached:
|
||||||
|
debugPrint("[APP STATE] detached");
|
||||||
|
ref.read(appStateProvider.notifier).state = AppStateEnum.detached;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> initApp() async {
|
||||||
|
// ! TOBE DELETE
|
||||||
|
// Simulate Sign In And Register/Get Device ID
|
||||||
|
// await ref.read(authenticationProvider.notifier).login();
|
||||||
|
// ref.read(backupProvider.notifier).getBackupInfo();
|
||||||
|
// WidgetsBinding.instance?.addObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
initState() {
|
||||||
|
super.initState();
|
||||||
|
initApp().then((_) => debugPrint("App Init Completed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
WidgetsBinding.instance?.removeObserver(this);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
final _immichRouter = AppRouter();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp.router(
|
||||||
|
title: 'Immich',
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
|
theme: ThemeData(
|
||||||
|
primarySwatch: Colors.indigo,
|
||||||
|
textTheme: GoogleFonts.workSansTextTheme(
|
||||||
|
Theme.of(context).textTheme.apply(fontSizeFactor: 1.0),
|
||||||
|
),
|
||||||
|
scaffoldBackgroundColor: const Color(0xFFf6f8fe),
|
||||||
|
appBarTheme: const AppBarTheme(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
foregroundColor: Colors.indigo,
|
||||||
|
elevation: 1,
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
routeInformationParser: _immichRouter.defaultRouteParser(),
|
||||||
|
routerDelegate: _immichRouter.delegate(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
0
mobile/lib/module_template/models/store_model_here.txt
Normal file
0
mobile/lib/module_template/ui/store_ui_here.txt
Normal file
0
mobile/lib/module_template/views/store_views_here.txt
Normal file
113
mobile/lib/modules/home/models/get_all_asset_respose.model.dart
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:immich_mobile/shared/models/immich_asset.model.dart';
|
||||||
|
|
||||||
|
class ImmichAssetGroupByDate {
|
||||||
|
final String date;
|
||||||
|
List<ImmichAsset> assets;
|
||||||
|
ImmichAssetGroupByDate({
|
||||||
|
required this.date,
|
||||||
|
required this.assets,
|
||||||
|
});
|
||||||
|
|
||||||
|
ImmichAssetGroupByDate copyWith({
|
||||||
|
String? date,
|
||||||
|
List<ImmichAsset>? assets,
|
||||||
|
}) {
|
||||||
|
return ImmichAssetGroupByDate(
|
||||||
|
date: date ?? this.date,
|
||||||
|
assets: assets ?? this.assets,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'date': date,
|
||||||
|
'assets': assets.map((x) => x.toMap()).toList(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory ImmichAssetGroupByDate.fromMap(Map<String, dynamic> map) {
|
||||||
|
return ImmichAssetGroupByDate(
|
||||||
|
date: map['date'] ?? '',
|
||||||
|
assets: List<ImmichAsset>.from(map['assets']?.map((x) => ImmichAsset.fromMap(x))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String toJson() => json.encode(toMap());
|
||||||
|
|
||||||
|
factory ImmichAssetGroupByDate.fromJson(String source) => ImmichAssetGroupByDate.fromMap(json.decode(source));
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'ImmichAssetGroupByDate(date: $date, assets: $assets)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
|
return other is ImmichAssetGroupByDate && other.date == date && listEquals(other.assets, assets);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => date.hashCode ^ assets.hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
class GetAllAssetResponse {
|
||||||
|
final int count;
|
||||||
|
final List<ImmichAssetGroupByDate> data;
|
||||||
|
final String nextPageKey;
|
||||||
|
GetAllAssetResponse({
|
||||||
|
required this.count,
|
||||||
|
required this.data,
|
||||||
|
required this.nextPageKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
GetAllAssetResponse copyWith({
|
||||||
|
int? count,
|
||||||
|
List<ImmichAssetGroupByDate>? data,
|
||||||
|
String? nextPageKey,
|
||||||
|
}) {
|
||||||
|
return GetAllAssetResponse(
|
||||||
|
count: count ?? this.count,
|
||||||
|
data: data ?? this.data,
|
||||||
|
nextPageKey: nextPageKey ?? this.nextPageKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'count': count,
|
||||||
|
'data': data.map((x) => x.toMap()).toList(),
|
||||||
|
'nextPageKey': nextPageKey,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory GetAllAssetResponse.fromMap(Map<String, dynamic> map) {
|
||||||
|
return GetAllAssetResponse(
|
||||||
|
count: map['count']?.toInt() ?? 0,
|
||||||
|
data: List<ImmichAssetGroupByDate>.from(map['data']?.map((x) => ImmichAssetGroupByDate.fromMap(x))),
|
||||||
|
nextPageKey: map['nextPageKey'] ?? '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String toJson() => json.encode(toMap());
|
||||||
|
|
||||||
|
factory GetAllAssetResponse.fromJson(String source) => GetAllAssetResponse.fromMap(json.decode(source));
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'GetAllAssetResponse(count: $count, data: $data, nextPageKey: $nextPageKey)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
|
return other is GetAllAssetResponse &&
|
||||||
|
other.count == count &&
|
||||||
|
listEquals(other.data, data) &&
|
||||||
|
other.nextPageKey == nextPageKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => count.hashCode ^ data.hashCode ^ nextPageKey.hashCode;
|
||||||
|
}
|
60
mobile/lib/modules/home/providers/asset.provider.dart
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/modules/home/models/get_all_asset_respose.model.dart';
|
||||||
|
import 'package:immich_mobile/modules/home/services/asset.service.dart';
|
||||||
|
|
||||||
|
class AssetNotifier extends StateNotifier<List<ImmichAssetGroupByDate>> {
|
||||||
|
final imagePerPage = 100;
|
||||||
|
final AssetService _assetService = AssetService();
|
||||||
|
|
||||||
|
AssetNotifier() : super([]);
|
||||||
|
late String? nextPageKey = "";
|
||||||
|
bool isFetching = false;
|
||||||
|
|
||||||
|
getImmichAssets() async {
|
||||||
|
GetAllAssetResponse? res = await _assetService.getAllAsset();
|
||||||
|
nextPageKey = res?.nextPageKey;
|
||||||
|
|
||||||
|
if (res != null) {
|
||||||
|
for (var assets in res.data) {
|
||||||
|
state = [...state, assets];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getMoreAsset() async {
|
||||||
|
if (nextPageKey != null && !isFetching) {
|
||||||
|
isFetching = true;
|
||||||
|
GetAllAssetResponse? res = await _assetService.getMoreAsset(nextPageKey);
|
||||||
|
|
||||||
|
if (res != null) {
|
||||||
|
nextPageKey = res.nextPageKey;
|
||||||
|
|
||||||
|
List<ImmichAssetGroupByDate> previousState = state;
|
||||||
|
List<ImmichAssetGroupByDate> currentState = [];
|
||||||
|
|
||||||
|
for (var assets in res.data) {
|
||||||
|
currentState = [...currentState, assets];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previousState.last.date == currentState.first.date) {
|
||||||
|
previousState.last.assets = [...previousState.last.assets, ...currentState.first.assets];
|
||||||
|
state = [...previousState, ...currentState.sublist(1)];
|
||||||
|
} else {
|
||||||
|
state = [...previousState, ...currentState];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isFetching = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clearAllAsset() {
|
||||||
|
state = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final currentLocalPageProvider = StateProvider<int>((ref) => 0);
|
||||||
|
|
||||||
|
final assetProvider = StateNotifierProvider<AssetNotifier, List<ImmichAssetGroupByDate>>((ref) {
|
||||||
|
return AssetNotifier();
|
||||||
|
});
|
38
mobile/lib/modules/home/services/asset.service.dart
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:immich_mobile/modules/home/models/get_all_asset_respose.model.dart';
|
||||||
|
import 'package:immich_mobile/shared/services/network.service.dart';
|
||||||
|
|
||||||
|
class AssetService {
|
||||||
|
final NetworkService _networkService = NetworkService();
|
||||||
|
|
||||||
|
Future<GetAllAssetResponse?> getAllAsset() async {
|
||||||
|
var res = await _networkService.getRequest(url: "asset/all");
|
||||||
|
try {
|
||||||
|
Map<String, dynamic> decodedData = jsonDecode(res.toString());
|
||||||
|
|
||||||
|
GetAllAssetResponse result = GetAllAssetResponse.fromMap(decodedData);
|
||||||
|
return result;
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint("Error getAllAsset ${e.toString()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<GetAllAssetResponse?> getMoreAsset(String? nextPageKey) async {
|
||||||
|
try {
|
||||||
|
var res = await _networkService.getRequest(
|
||||||
|
url: "asset/all?nextPageKey=$nextPageKey",
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> decodedData = jsonDecode(res.toString());
|
||||||
|
|
||||||
|
GetAllAssetResponse result = GetAllAssetResponse.fromMap(decodedData);
|
||||||
|
if (result.count != 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint("Error getAllAsset ${e.toString()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
mobile/lib/modules/home/ui/image_grid.dart
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:immich_mobile/modules/home/ui/thumbnail_image.dart';
|
||||||
|
import 'package:immich_mobile/shared/models/immich_asset.model.dart';
|
||||||
|
|
||||||
|
class ImageGrid extends StatelessWidget {
|
||||||
|
final List<ImmichAsset> assetGroup;
|
||||||
|
|
||||||
|
const ImageGrid({Key? key, required this.assetGroup}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SliverGrid(
|
||||||
|
gridDelegate:
|
||||||
|
const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, crossAxisSpacing: 5.0, mainAxisSpacing: 5),
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(BuildContext context, int index) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {},
|
||||||
|
child: ThumbnailImage(asset: assetGroup[index]),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
childCount: assetGroup.length,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
105
mobile/lib/modules/home/ui/immich_sliver_appbar.dart
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/modules/home/providers/asset.provider.dart';
|
||||||
|
|
||||||
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
|
import 'package:immich_mobile/shared/models/backup_state.model.dart';
|
||||||
|
import 'package:immich_mobile/shared/providers/backup.provider.dart';
|
||||||
|
|
||||||
|
class ImmichSliverAppBar extends ConsumerWidget {
|
||||||
|
const ImmichSliverAppBar({
|
||||||
|
Key? key,
|
||||||
|
required this.imageGridGroup,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final List<Widget> imageGridGroup;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final BackUpState _backupState = ref.watch(backupProvider);
|
||||||
|
|
||||||
|
return SliverPadding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
|
||||||
|
sliver: SliverAppBar(
|
||||||
|
centerTitle: true,
|
||||||
|
floating: true,
|
||||||
|
pinned: false,
|
||||||
|
snap: false,
|
||||||
|
backgroundColor: Colors.grey[200],
|
||||||
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))),
|
||||||
|
leading: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return IconButton(
|
||||||
|
icon: const Icon(Icons.account_circle_rounded),
|
||||||
|
onPressed: () {
|
||||||
|
Scaffold.of(context).openDrawer();
|
||||||
|
},
|
||||||
|
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
'IMMICH',
|
||||||
|
style: GoogleFonts.snowburstOne(
|
||||||
|
textStyle: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 18,
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
Stack(
|
||||||
|
alignment: AlignmentDirectional.center,
|
||||||
|
children: [
|
||||||
|
_backupState.backupProgress == BackUpProgressEnum.inProgress
|
||||||
|
? Positioned(
|
||||||
|
top: 10,
|
||||||
|
right: 12,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 8,
|
||||||
|
width: 8,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 1,
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(Theme.of(context).primaryColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.backup_rounded),
|
||||||
|
tooltip: 'Backup Controller',
|
||||||
|
onPressed: () async {
|
||||||
|
var onPop = await AutoRouter.of(context).push(const BackupControllerRoute());
|
||||||
|
|
||||||
|
// Fetch new image
|
||||||
|
if (onPop == true) {
|
||||||
|
// Remove and force getting new widget again
|
||||||
|
if (imageGridGroup.isNotEmpty) {
|
||||||
|
ref.read(assetProvider.notifier).getMoreAsset();
|
||||||
|
} else {
|
||||||
|
ref.read(assetProvider.notifier).getImmichAssets();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_backupState.backupProgress == BackUpProgressEnum.inProgress
|
||||||
|
? Positioned(
|
||||||
|
bottom: 5,
|
||||||
|
child: Text(
|
||||||
|
_backupState.backingUpAssetCount.toString(),
|
||||||
|
style: const TextStyle(fontSize: 9, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Container()
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
systemOverlayStyle: SystemUiOverlayStyle.dark,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
72
mobile/lib/modules/home/ui/profile_drawer.dart
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import 'package:auto_route/annotations.dart';
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/src/widgets/framework.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/modules/home/providers/asset.provider.dart';
|
||||||
|
import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
|
||||||
|
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
||||||
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
|
|
||||||
|
class ProfileDrawer extends ConsumerWidget {
|
||||||
|
const ProfileDrawer({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
AuthenticationState _authState = ref.watch(authenticationProvider);
|
||||||
|
|
||||||
|
return Drawer(
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topRight: Radius.circular(5),
|
||||||
|
bottomRight: Radius.circular(5),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: ListView(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
children: [
|
||||||
|
DrawerHeader(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey[200],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Image(
|
||||||
|
image: AssetImage('assets/immich-logo-no-outline.png'),
|
||||||
|
width: 50,
|
||||||
|
filterQuality: FilterQuality.high,
|
||||||
|
),
|
||||||
|
const Padding(padding: EdgeInsets.all(8)),
|
||||||
|
Text(
|
||||||
|
_authState.userEmail,
|
||||||
|
style: TextStyle(color: Theme.of(context).primaryColor, fontWeight: FontWeight.bold),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
tileColor: Colors.grey[100],
|
||||||
|
leading: const Icon(
|
||||||
|
Icons.logout_rounded,
|
||||||
|
color: Colors.black54,
|
||||||
|
),
|
||||||
|
title: const Text(
|
||||||
|
"Sign Out",
|
||||||
|
style: TextStyle(color: Colors.black54, fontSize: 14),
|
||||||
|
),
|
||||||
|
onTap: () async {
|
||||||
|
bool res = await ref.read(authenticationProvider.notifier).logout();
|
||||||
|
ref.read(assetProvider.notifier).clearAllAsset();
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
AutoRouter.of(context).popUntilRoot();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
52
mobile/lib/modules/home/ui/thumbnail_image.dart
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
|
import 'package:immich_mobile/constants/hive_box.dart';
|
||||||
|
import 'package:immich_mobile/shared/models/immich_asset.model.dart';
|
||||||
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
|
import 'package:transparent_image/transparent_image.dart';
|
||||||
|
|
||||||
|
class ThumbnailImage extends StatelessWidget {
|
||||||
|
final ImmichAsset asset;
|
||||||
|
|
||||||
|
const ThumbnailImage({Key? key, required this.asset}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var box = Hive.box(userInfoBox);
|
||||||
|
var thumbnailRequestUrl =
|
||||||
|
'${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=true';
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
AutoRouter.of(context).push(
|
||||||
|
ImageViewerRoute(
|
||||||
|
imageUrl:
|
||||||
|
'${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=false',
|
||||||
|
heroTag: asset.id,
|
||||||
|
thumbnailUrl: thumbnailRequestUrl,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onLongPress: () {},
|
||||||
|
child: Hero(
|
||||||
|
tag: asset.id,
|
||||||
|
child: CachedNetworkImage(
|
||||||
|
width: 300,
|
||||||
|
height: 300,
|
||||||
|
memCacheHeight: 250,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
imageUrl: thumbnailRequestUrl,
|
||||||
|
httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},
|
||||||
|
fadeInDuration: const Duration(milliseconds: 250),
|
||||||
|
progressIndicatorBuilder: (context, url, downloadProgress) => Transform.scale(
|
||||||
|
scale: 0.2,
|
||||||
|
child: CircularProgressIndicator(value: downloadProgress.progress),
|
||||||
|
),
|
||||||
|
errorWidget: (context, url, error) => const Icon(Icons.error),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
165
mobile/lib/modules/home/views/home_page.dart
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/modules/home/ui/immich_sliver_appbar.dart';
|
||||||
|
import 'package:immich_mobile/modules/home/ui/profile_drawer.dart';
|
||||||
|
import 'package:immich_mobile/shared/models/backup_state.model.dart';
|
||||||
|
import 'package:immich_mobile/modules/home/models/get_all_asset_respose.model.dart';
|
||||||
|
import 'package:immich_mobile/modules/home/ui/image_grid.dart';
|
||||||
|
import 'package:immich_mobile/modules/home/providers/asset.provider.dart';
|
||||||
|
import 'package:immich_mobile/shared/providers/backup.provider.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
class HomePage extends HookConsumerWidget {
|
||||||
|
const HomePage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final ValueNotifier<bool> _showBackToTopBtn = useState(false);
|
||||||
|
ScrollController _scrollController = useScrollController();
|
||||||
|
List<ImmichAssetGroupByDate> assetGroup = ref.watch(assetProvider);
|
||||||
|
BackUpState _backupState = ref.watch(backupProvider);
|
||||||
|
List<Widget> imageGridGroup = [];
|
||||||
|
List<GlobalKey> monthGroupKey = [];
|
||||||
|
|
||||||
|
_scrollControllerCallback() {
|
||||||
|
var endOfPage = _scrollController.position.maxScrollExtent;
|
||||||
|
|
||||||
|
if (_scrollController.offset >= endOfPage - (endOfPage * 0.1) && !_scrollController.position.outOfRange) {
|
||||||
|
ref.read(assetProvider.notifier).getMoreAsset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_scrollController.offset >= 400) {
|
||||||
|
_showBackToTopBtn.value = true;
|
||||||
|
} else {
|
||||||
|
_showBackToTopBtn.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
ref.read(assetProvider.notifier).getImmichAssets();
|
||||||
|
|
||||||
|
_scrollController.addListener(_scrollControllerCallback);
|
||||||
|
|
||||||
|
return () => _scrollController.removeListener(_scrollControllerCallback);
|
||||||
|
}, [_scrollController, key]);
|
||||||
|
|
||||||
|
Widget _buildBody() {
|
||||||
|
if (assetGroup.isNotEmpty) {
|
||||||
|
String lastGroupDate = assetGroup[0].date;
|
||||||
|
|
||||||
|
for (var group in assetGroup) {
|
||||||
|
var dateTitle = group.date;
|
||||||
|
var assetGroup = group.assets;
|
||||||
|
|
||||||
|
int? currentMonth = DateTime.tryParse(dateTitle)?.month;
|
||||||
|
int? previousMonth = DateTime.tryParse(lastGroupDate)?.month;
|
||||||
|
|
||||||
|
if ((currentMonth! - previousMonth!) != 0) {
|
||||||
|
var myKey = GlobalKey();
|
||||||
|
monthGroupKey.add(myKey);
|
||||||
|
// debugPrint("Group Key $myKey");
|
||||||
|
|
||||||
|
imageGridGroup.add(
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
key: myKey,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 10.0, top: 32),
|
||||||
|
child: Text(
|
||||||
|
DateFormat('MMMM, y').format(
|
||||||
|
DateTime.parse(dateTitle),
|
||||||
|
),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
imageGridGroup.add(
|
||||||
|
_buildDateGroupTitle(dateTitle),
|
||||||
|
);
|
||||||
|
|
||||||
|
imageGridGroup.add(ImageGrid(assetGroup: assetGroup));
|
||||||
|
|
||||||
|
lastGroupDate = dateTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SafeArea(
|
||||||
|
child: CustomScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
|
slivers: [
|
||||||
|
ImmichSliverAppBar(imageGridGroup: imageGridGroup),
|
||||||
|
...imageGridGroup,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
drawer: const ProfileDrawer(),
|
||||||
|
body: _buildBody(),
|
||||||
|
bottomNavigationBar: BottomAppBar(
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (monthGroupKey.isNotEmpty) {
|
||||||
|
var targetContext = monthGroupKey.last.currentContext;
|
||||||
|
if (targetContext != null) {
|
||||||
|
Scrollable.ensureVisible(
|
||||||
|
targetContext,
|
||||||
|
duration: const Duration(milliseconds: 400),
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.ac_unit_outlined),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
floatingActionButton: _showBackToTopBtn.value
|
||||||
|
? FloatingActionButton.small(
|
||||||
|
enableFeedback: true,
|
||||||
|
backgroundColor: Theme.of(context).secondaryHeaderColor,
|
||||||
|
foregroundColor: Theme.of(context).primaryColor,
|
||||||
|
onPressed: () {
|
||||||
|
_scrollController.animateTo(0, duration: const Duration(seconds: 1), curve: Curves.easeOutExpo);
|
||||||
|
},
|
||||||
|
child: const Icon(Icons.keyboard_arrow_up_rounded),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
SliverToBoxAdapter _buildDateGroupTitle(String dateTitle) {
|
||||||
|
var currentYear = DateTime.now().year;
|
||||||
|
var groupYear = DateTime.parse(dateTitle).year;
|
||||||
|
var formatDateTemplate = currentYear == groupYear ? 'E, MMM dd' : 'E, MMM dd, yyyy';
|
||||||
|
return SliverToBoxAdapter(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 24.0, bottom: 24.0, left: 3.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8.0, bottom: 5.0, top: 5.0),
|
||||||
|
child: Text(
|
||||||
|
DateFormat(formatDateTemplate).format(DateTime.parse(dateTitle)),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:immich_mobile/shared/models/device_info.model.dart';
|
||||||
|
|
||||||
|
class AuthenticationState {
|
||||||
|
final String deviceId;
|
||||||
|
final String deviceType;
|
||||||
|
final String userId;
|
||||||
|
final String userEmail;
|
||||||
|
final bool isAuthenticated;
|
||||||
|
final DeviceInfoRemote deviceInfo;
|
||||||
|
|
||||||
|
AuthenticationState({
|
||||||
|
required this.deviceId,
|
||||||
|
required this.deviceType,
|
||||||
|
required this.userId,
|
||||||
|
required this.userEmail,
|
||||||
|
required this.isAuthenticated,
|
||||||
|
required this.deviceInfo,
|
||||||
|
});
|
||||||
|
|
||||||
|
AuthenticationState copyWith({
|
||||||
|
String? deviceId,
|
||||||
|
String? deviceType,
|
||||||
|
String? userId,
|
||||||
|
String? userEmail,
|
||||||
|
bool? isAuthenticated,
|
||||||
|
DeviceInfoRemote? deviceInfo,
|
||||||
|
}) {
|
||||||
|
return AuthenticationState(
|
||||||
|
deviceId: deviceId ?? this.deviceId,
|
||||||
|
deviceType: deviceType ?? this.deviceType,
|
||||||
|
userId: userId ?? this.userId,
|
||||||
|
userEmail: userEmail ?? this.userEmail,
|
||||||
|
isAuthenticated: isAuthenticated ?? this.isAuthenticated,
|
||||||
|
deviceInfo: deviceInfo ?? this.deviceInfo,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'AuthenticationState(deviceId: $deviceId, deviceType: $deviceType, userId: $userId, userEmail: $userEmail, isAuthenticated: $isAuthenticated, deviceInfo: $deviceInfo)';
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'deviceId': deviceId,
|
||||||
|
'deviceType': deviceType,
|
||||||
|
'userId': userId,
|
||||||
|
'userEmail': userEmail,
|
||||||
|
'isAuthenticated': isAuthenticated,
|
||||||
|
'deviceInfo': deviceInfo.toMap(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory AuthenticationState.fromMap(Map<String, dynamic> map) {
|
||||||
|
return AuthenticationState(
|
||||||
|
deviceId: map['deviceId'] ?? '',
|
||||||
|
deviceType: map['deviceType'] ?? '',
|
||||||
|
userId: map['userId'] ?? '',
|
||||||
|
userEmail: map['userEmail'] ?? '',
|
||||||
|
isAuthenticated: map['isAuthenticated'] ?? false,
|
||||||
|
deviceInfo: DeviceInfoRemote.fromMap(map['deviceInfo']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String toJson() => json.encode(toMap());
|
||||||
|
|
||||||
|
factory AuthenticationState.fromJson(String source) => AuthenticationState.fromMap(json.decode(source));
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
|
return other is AuthenticationState &&
|
||||||
|
other.deviceId == deviceId &&
|
||||||
|
other.deviceType == deviceType &&
|
||||||
|
other.userId == userId &&
|
||||||
|
other.userEmail == userEmail &&
|
||||||
|
other.isAuthenticated == isAuthenticated &&
|
||||||
|
other.deviceInfo == deviceInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return deviceId.hashCode ^
|
||||||
|
deviceType.hashCode ^
|
||||||
|
userId.hashCode ^
|
||||||
|
userEmail.hashCode ^
|
||||||
|
isAuthenticated.hashCode ^
|
||||||
|
deviceInfo.hashCode;
|
||||||
|
}
|
||||||
|
}
|
61
mobile/lib/modules/login/models/login_response.model.dart
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
class LogInReponse {
|
||||||
|
final String accessToken;
|
||||||
|
final String userId;
|
||||||
|
final String userEmail;
|
||||||
|
|
||||||
|
LogInReponse({
|
||||||
|
required this.accessToken,
|
||||||
|
required this.userId,
|
||||||
|
required this.userEmail,
|
||||||
|
});
|
||||||
|
|
||||||
|
LogInReponse copyWith({
|
||||||
|
String? accessToken,
|
||||||
|
String? userId,
|
||||||
|
String? userEmail,
|
||||||
|
}) {
|
||||||
|
return LogInReponse(
|
||||||
|
accessToken: accessToken ?? this.accessToken,
|
||||||
|
userId: userId ?? this.userId,
|
||||||
|
userEmail: userEmail ?? this.userEmail,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'accessToken': accessToken,
|
||||||
|
'userId': userId,
|
||||||
|
'userEmail': userEmail,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory LogInReponse.fromMap(Map<String, dynamic> map) {
|
||||||
|
return LogInReponse(
|
||||||
|
accessToken: map['accessToken'] ?? '',
|
||||||
|
userId: map['userId'] ?? '',
|
||||||
|
userEmail: map['userEmail'] ?? '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String toJson() => json.encode(toMap());
|
||||||
|
|
||||||
|
factory LogInReponse.fromJson(String source) => LogInReponse.fromMap(json.decode(source));
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'LogInReponse(accessToken: $accessToken, userId: $userId, userEmail: $userEmail)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
|
return other is LogInReponse &&
|
||||||
|
other.accessToken == accessToken &&
|
||||||
|
other.userId == userId &&
|
||||||
|
other.userEmail == userEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => accessToken.hashCode ^ userId.hashCode ^ userEmail.hashCode;
|
||||||
|
}
|
127
mobile/lib/modules/login/providers/authentication.provider.dart
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/constants/hive_box.dart';
|
||||||
|
import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
|
||||||
|
import 'package:immich_mobile/modules/login/models/login_response.model.dart';
|
||||||
|
import 'package:immich_mobile/shared/services/backup.service.dart';
|
||||||
|
import 'package:immich_mobile/shared/services/device_info.service.dart';
|
||||||
|
import 'package:immich_mobile/shared/services/network.service.dart';
|
||||||
|
import 'package:immich_mobile/shared/models/device_info.model.dart';
|
||||||
|
import 'package:immich_mobile/utils/dio_http_interceptor.dart';
|
||||||
|
|
||||||
|
class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
|
||||||
|
AuthenticationNotifier()
|
||||||
|
: super(
|
||||||
|
AuthenticationState(
|
||||||
|
deviceId: "",
|
||||||
|
deviceType: "",
|
||||||
|
isAuthenticated: false,
|
||||||
|
userId: "",
|
||||||
|
userEmail: "",
|
||||||
|
deviceInfo: DeviceInfoRemote(
|
||||||
|
id: 0,
|
||||||
|
userId: "",
|
||||||
|
deviceId: "",
|
||||||
|
deviceType: "",
|
||||||
|
notificationToken: "",
|
||||||
|
createdAt: "",
|
||||||
|
isAutoBackup: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final DeviceInfoService _deviceInfoService = DeviceInfoService();
|
||||||
|
final BackupService _backupService = BackupService();
|
||||||
|
final NetworkService _networkService = NetworkService();
|
||||||
|
|
||||||
|
Future<bool> login(String email, String password, String serverEndpoint) async {
|
||||||
|
// Store server endpoint to Hive and test endpoint
|
||||||
|
if (serverEndpoint[serverEndpoint.length - 1] == "/") {
|
||||||
|
var validUrl = serverEndpoint.substring(0, serverEndpoint.length - 1);
|
||||||
|
Hive.box(userInfoBox).put(serverEndpointKey, validUrl);
|
||||||
|
} else {
|
||||||
|
Hive.box(userInfoBox).put(serverEndpointKey, serverEndpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isServerEndpointVerified = await _networkService.pingServer();
|
||||||
|
if (!isServerEndpointVerified) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store device id to local storage
|
||||||
|
var deviceInfo = await _deviceInfoService.getDeviceInfo();
|
||||||
|
Hive.box(userInfoBox).put(deviceIdKey, deviceInfo["deviceId"]);
|
||||||
|
|
||||||
|
state = state.copyWith(
|
||||||
|
deviceId: deviceInfo["deviceId"],
|
||||||
|
deviceType: deviceInfo["deviceType"],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Make sign-in request
|
||||||
|
try {
|
||||||
|
Response res = await _networkService.postRequest(url: 'auth/login', data: {'email': email, 'password': password});
|
||||||
|
|
||||||
|
var payload = LogInReponse.fromJson(res.toString());
|
||||||
|
|
||||||
|
Hive.box(userInfoBox).put(accessTokenKey, payload.accessToken);
|
||||||
|
|
||||||
|
state = state.copyWith(
|
||||||
|
isAuthenticated: true,
|
||||||
|
userId: payload.userId,
|
||||||
|
userEmail: payload.userEmail,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register device info
|
||||||
|
try {
|
||||||
|
Response res = await _networkService
|
||||||
|
.postRequest(url: 'device-info', data: {'deviceId': state.deviceId, 'deviceType': state.deviceType});
|
||||||
|
|
||||||
|
DeviceInfoRemote deviceInfo = DeviceInfoRemote.fromJson(res.toString());
|
||||||
|
state = state.copyWith(deviceInfo: deviceInfo);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint("ERROR Register Device Info: $e");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> logout() async {
|
||||||
|
Hive.box(userInfoBox).delete(accessTokenKey);
|
||||||
|
state = AuthenticationState(
|
||||||
|
deviceId: "",
|
||||||
|
deviceType: "",
|
||||||
|
isAuthenticated: false,
|
||||||
|
userId: "",
|
||||||
|
userEmail: "",
|
||||||
|
deviceInfo: DeviceInfoRemote(
|
||||||
|
id: 0,
|
||||||
|
userId: "",
|
||||||
|
deviceId: "",
|
||||||
|
deviceType: "",
|
||||||
|
notificationToken: "",
|
||||||
|
createdAt: "",
|
||||||
|
isAutoBackup: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
setAutoBackup(bool backupState) async {
|
||||||
|
var deviceInfo = await _deviceInfoService.getDeviceInfo();
|
||||||
|
var deviceId = deviceInfo["deviceId"];
|
||||||
|
var deviceType = deviceInfo["deviceType"];
|
||||||
|
|
||||||
|
DeviceInfoRemote deviceInfoRemote = await _backupService.setAutoBackup(backupState, deviceId, deviceType);
|
||||||
|
state = state.copyWith(deviceInfo: deviceInfoRemote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final authenticationProvider = StateNotifierProvider<AuthenticationNotifier, AuthenticationState>((ref) {
|
||||||
|
return AuthenticationNotifier();
|
||||||
|
});
|
124
mobile/lib/modules/login/ui/login_form.dart
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
||||||
|
|
||||||
|
class LoginForm extends HookConsumerWidget {
|
||||||
|
const LoginForm({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final usernameController = useTextEditingController(text: 'testuser@email.com');
|
||||||
|
final passwordController = useTextEditingController(text: 'password');
|
||||||
|
final serverEndpointController = useTextEditingController(text: 'http://192.168.1.216');
|
||||||
|
|
||||||
|
return Center(
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(maxWidth: 300),
|
||||||
|
child: Wrap(
|
||||||
|
spacing: 32,
|
||||||
|
runSpacing: 32,
|
||||||
|
alignment: WrapAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Image(
|
||||||
|
image: AssetImage('assets/immich-logo-no-outline.png'),
|
||||||
|
width: 128,
|
||||||
|
filterQuality: FilterQuality.high,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'IMMICH',
|
||||||
|
style: GoogleFonts.snowburstOne(
|
||||||
|
textStyle:
|
||||||
|
TextStyle(fontWeight: FontWeight.bold, fontSize: 48, color: Theme.of(context).primaryColor)),
|
||||||
|
),
|
||||||
|
EmailInput(controller: usernameController),
|
||||||
|
PasswordInput(controller: passwordController),
|
||||||
|
ServerEndpointInput(controller: serverEndpointController),
|
||||||
|
LoginButton(
|
||||||
|
emailController: usernameController,
|
||||||
|
passwordController: passwordController,
|
||||||
|
serverEndpointController: serverEndpointController,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ServerEndpointInput extends StatelessWidget {
|
||||||
|
final TextEditingController controller;
|
||||||
|
|
||||||
|
const ServerEndpointInput({Key? key, required this.controller}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return TextFormField(
|
||||||
|
controller: controller,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Server Endpoint URL', border: OutlineInputBorder(), hintText: 'http://your-server-ip:port'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EmailInput extends StatelessWidget {
|
||||||
|
final TextEditingController controller;
|
||||||
|
|
||||||
|
const EmailInput({Key? key, required this.controller}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return TextFormField(
|
||||||
|
controller: controller,
|
||||||
|
decoration:
|
||||||
|
const InputDecoration(labelText: 'email', border: OutlineInputBorder(), hintText: 'youremail@email.com'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PasswordInput extends StatelessWidget {
|
||||||
|
final TextEditingController controller;
|
||||||
|
|
||||||
|
const PasswordInput({Key? key, required this.controller}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return TextFormField(
|
||||||
|
obscureText: true,
|
||||||
|
controller: controller,
|
||||||
|
decoration: const InputDecoration(labelText: 'Password', border: OutlineInputBorder(), hintText: 'password'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoginButton extends ConsumerWidget {
|
||||||
|
final TextEditingController emailController;
|
||||||
|
final TextEditingController passwordController;
|
||||||
|
final TextEditingController serverEndpointController;
|
||||||
|
|
||||||
|
const LoginButton(
|
||||||
|
{Key? key,
|
||||||
|
required this.emailController,
|
||||||
|
required this.passwordController,
|
||||||
|
required this.serverEndpointController})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
return ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
var isAuthenicated = await ref
|
||||||
|
.read(authenticationProvider.notifier)
|
||||||
|
.login(emailController.text, passwordController.text, serverEndpointController.text);
|
||||||
|
|
||||||
|
if (isAuthenicated) {
|
||||||
|
AutoRouter.of(context).pushNamed("/home-page");
|
||||||
|
} else {
|
||||||
|
debugPrint("BAD LOGIN TRY AGAIN - Show UI Here");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const Text("Login"));
|
||||||
|
}
|
||||||
|
}
|
16
mobile/lib/modules/login/views/login_page.dart
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
||||||
|
import 'package:immich_mobile/modules/login/ui/login_form.dart';
|
||||||
|
|
||||||
|
class LoginPage extends HookConsumerWidget {
|
||||||
|
const LoginPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
return const Scaffold(
|
||||||
|
body: LoginForm(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
22
mobile/lib/routing/auth_guard.dart
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:immich_mobile/shared/services/network.service.dart';
|
||||||
|
|
||||||
|
class AuthGuard extends AutoRouteGuard {
|
||||||
|
final NetworkService _networkService = NetworkService();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onNavigation(NavigationResolver resolver, StackRouter router) async {
|
||||||
|
try {
|
||||||
|
var res = await _networkService.postRequest(url: 'auth/validateToken');
|
||||||
|
var jsonReponse = jsonDecode(res.toString());
|
||||||
|
if (jsonReponse['authStatus']) {
|
||||||
|
resolver.next(true);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
router.removeUntil((route) => route.name == "LoginRoute");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
mobile/lib/routing/router.dart
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:immich_mobile/modules/login/views/login_page.dart';
|
||||||
|
import 'package:immich_mobile/modules/home/views/home_page.dart';
|
||||||
|
import 'package:immich_mobile/routing/auth_guard.dart';
|
||||||
|
import 'package:immich_mobile/shared/views/backup_controller_page.dart';
|
||||||
|
import 'package:immich_mobile/shared/views/image_viewer_page.dart';
|
||||||
|
|
||||||
|
part 'router.gr.dart';
|
||||||
|
|
||||||
|
@MaterialAutoRouter(
|
||||||
|
replaceInRouteName: 'Page,Route',
|
||||||
|
routes: <AutoRoute>[
|
||||||
|
AutoRoute(page: LoginPage, initial: true),
|
||||||
|
AutoRoute(page: HomePage, guards: [AuthGuard]),
|
||||||
|
AutoRoute(page: BackupControllerPage, guards: [AuthGuard]),
|
||||||
|
AutoRoute(page: ImageViewerPage, guards: [AuthGuard]),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
class AppRouter extends _$AppRouter {
|
||||||
|
AppRouter() : super(authGuard: AuthGuard());
|
||||||
|
}
|
122
mobile/lib/routing/router.gr.dart
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
// **************************************************************************
|
||||||
|
// AutoRouteGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// AutoRouteGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
//
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
|
||||||
|
part of 'router.dart';
|
||||||
|
|
||||||
|
class _$AppRouter extends RootStackRouter {
|
||||||
|
_$AppRouter(
|
||||||
|
{GlobalKey<NavigatorState>? navigatorKey, required this.authGuard})
|
||||||
|
: super(navigatorKey);
|
||||||
|
|
||||||
|
final AuthGuard authGuard;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final Map<String, PageFactory> pagesMap = {
|
||||||
|
LoginRoute.name: (routeData) {
|
||||||
|
return MaterialPageX<dynamic>(
|
||||||
|
routeData: routeData, child: const LoginPage());
|
||||||
|
},
|
||||||
|
HomeRoute.name: (routeData) {
|
||||||
|
return MaterialPageX<dynamic>(
|
||||||
|
routeData: routeData, child: const HomePage());
|
||||||
|
},
|
||||||
|
BackupControllerRoute.name: (routeData) {
|
||||||
|
return MaterialPageX<dynamic>(
|
||||||
|
routeData: routeData, child: const BackupControllerPage());
|
||||||
|
},
|
||||||
|
ImageViewerRoute.name: (routeData) {
|
||||||
|
final args = routeData.argsAs<ImageViewerRouteArgs>();
|
||||||
|
return MaterialPageX<dynamic>(
|
||||||
|
routeData: routeData,
|
||||||
|
child: ImageViewerPage(
|
||||||
|
key: args.key,
|
||||||
|
imageUrl: args.imageUrl,
|
||||||
|
heroTag: args.heroTag,
|
||||||
|
thumbnailUrl: args.thumbnailUrl));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<RouteConfig> get routes => [
|
||||||
|
RouteConfig(LoginRoute.name, path: '/'),
|
||||||
|
RouteConfig(HomeRoute.name, path: '/home-page', guards: [authGuard]),
|
||||||
|
RouteConfig(BackupControllerRoute.name,
|
||||||
|
path: '/backup-controller-page', guards: [authGuard]),
|
||||||
|
RouteConfig(ImageViewerRoute.name,
|
||||||
|
path: '/image-viewer-page', guards: [authGuard])
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [LoginPage]
|
||||||
|
class LoginRoute extends PageRouteInfo<void> {
|
||||||
|
const LoginRoute() : super(LoginRoute.name, path: '/');
|
||||||
|
|
||||||
|
static const String name = 'LoginRoute';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [HomePage]
|
||||||
|
class HomeRoute extends PageRouteInfo<void> {
|
||||||
|
const HomeRoute() : super(HomeRoute.name, path: '/home-page');
|
||||||
|
|
||||||
|
static const String name = 'HomeRoute';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [BackupControllerPage]
|
||||||
|
class BackupControllerRoute extends PageRouteInfo<void> {
|
||||||
|
const BackupControllerRoute()
|
||||||
|
: super(BackupControllerRoute.name, path: '/backup-controller-page');
|
||||||
|
|
||||||
|
static const String name = 'BackupControllerRoute';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [ImageViewerPage]
|
||||||
|
class ImageViewerRoute extends PageRouteInfo<ImageViewerRouteArgs> {
|
||||||
|
ImageViewerRoute(
|
||||||
|
{Key? key,
|
||||||
|
required String imageUrl,
|
||||||
|
required String heroTag,
|
||||||
|
required String thumbnailUrl})
|
||||||
|
: super(ImageViewerRoute.name,
|
||||||
|
path: '/image-viewer-page',
|
||||||
|
args: ImageViewerRouteArgs(
|
||||||
|
key: key,
|
||||||
|
imageUrl: imageUrl,
|
||||||
|
heroTag: heroTag,
|
||||||
|
thumbnailUrl: thumbnailUrl));
|
||||||
|
|
||||||
|
static const String name = 'ImageViewerRoute';
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImageViewerRouteArgs {
|
||||||
|
const ImageViewerRouteArgs(
|
||||||
|
{this.key,
|
||||||
|
required this.imageUrl,
|
||||||
|
required this.heroTag,
|
||||||
|
required this.thumbnailUrl});
|
||||||
|
|
||||||
|
final Key? key;
|
||||||
|
|
||||||
|
final String imageUrl;
|
||||||
|
|
||||||
|
final String heroTag;
|
||||||
|
|
||||||
|
final String thumbnailUrl;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'ImageViewerRouteArgs{key: $key, imageUrl: $imageUrl, heroTag: $heroTag, thumbnailUrl: $thumbnailUrl}';
|
||||||
|
}
|
||||||
|
}
|
77
mobile/lib/shared/models/backup_state.model.dart
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
|
||||||
|
import 'package:immich_mobile/shared/models/server_info.model.dart';
|
||||||
|
|
||||||
|
enum BackUpProgressEnum { idle, inProgress, done }
|
||||||
|
|
||||||
|
class BackUpState {
|
||||||
|
final BackUpProgressEnum backupProgress;
|
||||||
|
final int totalAssetCount;
|
||||||
|
final int assetOnDatabase;
|
||||||
|
final int backingUpAssetCount;
|
||||||
|
final double progressInPercentage;
|
||||||
|
final CancelToken cancelToken;
|
||||||
|
final ServerInfo serverInfo;
|
||||||
|
|
||||||
|
BackUpState({
|
||||||
|
required this.backupProgress,
|
||||||
|
required this.totalAssetCount,
|
||||||
|
required this.assetOnDatabase,
|
||||||
|
required this.backingUpAssetCount,
|
||||||
|
required this.progressInPercentage,
|
||||||
|
required this.cancelToken,
|
||||||
|
required this.serverInfo,
|
||||||
|
});
|
||||||
|
|
||||||
|
BackUpState copyWith({
|
||||||
|
BackUpProgressEnum? backupProgress,
|
||||||
|
int? totalAssetCount,
|
||||||
|
int? assetOnDatabase,
|
||||||
|
int? backingUpAssetCount,
|
||||||
|
double? progressInPercentage,
|
||||||
|
CancelToken? cancelToken,
|
||||||
|
ServerInfo? serverInfo,
|
||||||
|
}) {
|
||||||
|
return BackUpState(
|
||||||
|
backupProgress: backupProgress ?? this.backupProgress,
|
||||||
|
totalAssetCount: totalAssetCount ?? this.totalAssetCount,
|
||||||
|
assetOnDatabase: assetOnDatabase ?? this.assetOnDatabase,
|
||||||
|
backingUpAssetCount: backingUpAssetCount ?? this.backingUpAssetCount,
|
||||||
|
progressInPercentage: progressInPercentage ?? this.progressInPercentage,
|
||||||
|
cancelToken: cancelToken ?? this.cancelToken,
|
||||||
|
serverInfo: serverInfo ?? this.serverInfo,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'BackUpState(backupProgress: $backupProgress, totalAssetCount: $totalAssetCount, assetOnDatabase: $assetOnDatabase, backingUpAssetCount: $backingUpAssetCount, progressInPercentage: $progressInPercentage, cancelToken: $cancelToken, serverInfo: $serverInfo)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
|
return other is BackUpState &&
|
||||||
|
other.backupProgress == backupProgress &&
|
||||||
|
other.totalAssetCount == totalAssetCount &&
|
||||||
|
other.assetOnDatabase == assetOnDatabase &&
|
||||||
|
other.backingUpAssetCount == backingUpAssetCount &&
|
||||||
|
other.progressInPercentage == progressInPercentage &&
|
||||||
|
other.cancelToken == cancelToken &&
|
||||||
|
other.serverInfo == serverInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return backupProgress.hashCode ^
|
||||||
|
totalAssetCount.hashCode ^
|
||||||
|
assetOnDatabase.hashCode ^
|
||||||
|
backingUpAssetCount.hashCode ^
|
||||||
|
progressInPercentage.hashCode ^
|
||||||
|
cancelToken.hashCode ^
|
||||||
|
serverInfo.hashCode;
|
||||||
|
}
|
||||||
|
}
|
100
mobile/lib/shared/models/device_info.model.dart
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:ffi';
|
||||||
|
|
||||||
|
class DeviceInfoRemote {
|
||||||
|
final int id;
|
||||||
|
final String userId;
|
||||||
|
final String deviceId;
|
||||||
|
final String deviceType;
|
||||||
|
final String notificationToken;
|
||||||
|
final String createdAt;
|
||||||
|
final bool isAutoBackup;
|
||||||
|
|
||||||
|
DeviceInfoRemote({
|
||||||
|
required this.id,
|
||||||
|
required this.userId,
|
||||||
|
required this.deviceId,
|
||||||
|
required this.deviceType,
|
||||||
|
required this.notificationToken,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.isAutoBackup,
|
||||||
|
});
|
||||||
|
|
||||||
|
DeviceInfoRemote copyWith({
|
||||||
|
int? id,
|
||||||
|
String? userId,
|
||||||
|
String? deviceId,
|
||||||
|
String? deviceType,
|
||||||
|
String? notificationToken,
|
||||||
|
String? createdAt,
|
||||||
|
bool? isAutoBackup,
|
||||||
|
}) {
|
||||||
|
return DeviceInfoRemote(
|
||||||
|
id: id ?? this.id,
|
||||||
|
userId: userId ?? this.userId,
|
||||||
|
deviceId: deviceId ?? this.deviceId,
|
||||||
|
deviceType: deviceType ?? this.deviceType,
|
||||||
|
notificationToken: notificationToken ?? this.notificationToken,
|
||||||
|
createdAt: createdAt ?? this.createdAt,
|
||||||
|
isAutoBackup: isAutoBackup ?? this.isAutoBackup,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'id': id,
|
||||||
|
'userId': userId,
|
||||||
|
'deviceId': deviceId,
|
||||||
|
'deviceType': deviceType,
|
||||||
|
'notificationToken': notificationToken,
|
||||||
|
'createdAt': createdAt,
|
||||||
|
'isAutoBackup': isAutoBackup,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory DeviceInfoRemote.fromMap(Map<String, dynamic> map) {
|
||||||
|
return DeviceInfoRemote(
|
||||||
|
id: map['id']?.toInt() ?? 0,
|
||||||
|
userId: map['userId'] ?? '',
|
||||||
|
deviceId: map['deviceId'] ?? '',
|
||||||
|
deviceType: map['deviceType'] ?? '',
|
||||||
|
notificationToken: map['notificationToken'] ?? '',
|
||||||
|
createdAt: map['createdAt'] ?? '',
|
||||||
|
isAutoBackup: map['isAutoBackup'] ?? false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String toJson() => json.encode(toMap());
|
||||||
|
|
||||||
|
factory DeviceInfoRemote.fromJson(String source) => DeviceInfoRemote.fromMap(json.decode(source));
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'DeviceInfo(id: $id, userId: $userId, deviceId: $deviceId, deviceType: $deviceType, notificationToken: $notificationToken, createdAt: $createdAt, isAutoBackup: $isAutoBackup)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
|
return other is DeviceInfoRemote &&
|
||||||
|
other.id == id &&
|
||||||
|
other.userId == userId &&
|
||||||
|
other.deviceId == deviceId &&
|
||||||
|
other.deviceType == deviceType &&
|
||||||
|
other.notificationToken == notificationToken &&
|
||||||
|
other.createdAt == createdAt &&
|
||||||
|
other.isAutoBackup == isAutoBackup;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return id.hashCode ^
|
||||||
|
userId.hashCode ^
|
||||||
|
deviceId.hashCode ^
|
||||||
|
deviceType.hashCode ^
|
||||||
|
notificationToken.hashCode ^
|
||||||
|
createdAt.hashCode ^
|
||||||
|
isAutoBackup.hashCode;
|
||||||
|
}
|
||||||
|
}
|
11
mobile/lib/shared/models/image_viewer_page_data.model.dart
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
class ImageViewerPageData {
|
||||||
|
final String heroTag;
|
||||||
|
final String imageUrl;
|
||||||
|
final String thumbnailUrl;
|
||||||
|
|
||||||
|
ImageViewerPageData({
|
||||||
|
required this.heroTag,
|
||||||
|
required this.imageUrl,
|
||||||
|
required this.thumbnailUrl,
|
||||||
|
});
|
||||||
|
}
|
131
mobile/lib/shared/models/immich_asset.model.dart
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
class ImmichAsset {
|
||||||
|
final String id;
|
||||||
|
final String deviceAssetId;
|
||||||
|
final String userId;
|
||||||
|
final String deviceId;
|
||||||
|
final String assetType;
|
||||||
|
final String localPath;
|
||||||
|
final String remotePath;
|
||||||
|
final String createdAt;
|
||||||
|
final String modifiedAt;
|
||||||
|
final bool isFavorite;
|
||||||
|
final String? description;
|
||||||
|
|
||||||
|
ImmichAsset({
|
||||||
|
required this.id,
|
||||||
|
required this.deviceAssetId,
|
||||||
|
required this.userId,
|
||||||
|
required this.deviceId,
|
||||||
|
required this.assetType,
|
||||||
|
required this.localPath,
|
||||||
|
required this.remotePath,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.modifiedAt,
|
||||||
|
required this.isFavorite,
|
||||||
|
this.description,
|
||||||
|
});
|
||||||
|
|
||||||
|
ImmichAsset copyWith({
|
||||||
|
String? id,
|
||||||
|
String? deviceAssetId,
|
||||||
|
String? userId,
|
||||||
|
String? deviceId,
|
||||||
|
String? assetType,
|
||||||
|
String? localPath,
|
||||||
|
String? remotePath,
|
||||||
|
String? createdAt,
|
||||||
|
String? modifiedAt,
|
||||||
|
bool? isFavorite,
|
||||||
|
String? description,
|
||||||
|
}) {
|
||||||
|
return ImmichAsset(
|
||||||
|
id: id ?? this.id,
|
||||||
|
deviceAssetId: deviceAssetId ?? this.deviceAssetId,
|
||||||
|
userId: userId ?? this.userId,
|
||||||
|
deviceId: deviceId ?? this.deviceId,
|
||||||
|
assetType: assetType ?? this.assetType,
|
||||||
|
localPath: localPath ?? this.localPath,
|
||||||
|
remotePath: remotePath ?? this.remotePath,
|
||||||
|
createdAt: createdAt ?? this.createdAt,
|
||||||
|
modifiedAt: modifiedAt ?? this.modifiedAt,
|
||||||
|
isFavorite: isFavorite ?? this.isFavorite,
|
||||||
|
description: description ?? this.description,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'id': id,
|
||||||
|
'deviceAssetId': deviceAssetId,
|
||||||
|
'userId': userId,
|
||||||
|
'deviceId': deviceId,
|
||||||
|
'assetType': assetType,
|
||||||
|
'localPath': localPath,
|
||||||
|
'remotePath': remotePath,
|
||||||
|
'createdAt': createdAt,
|
||||||
|
'modifiedAt': modifiedAt,
|
||||||
|
'isFavorite': isFavorite,
|
||||||
|
'description': description,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory ImmichAsset.fromMap(Map<String, dynamic> map) {
|
||||||
|
return ImmichAsset(
|
||||||
|
id: map['id'] ?? '',
|
||||||
|
deviceAssetId: map['deviceAssetId'] ?? '',
|
||||||
|
userId: map['userId'] ?? '',
|
||||||
|
deviceId: map['deviceId'] ?? '',
|
||||||
|
assetType: map['assetType'] ?? '',
|
||||||
|
localPath: map['localPath'] ?? '',
|
||||||
|
remotePath: map['remotePath'] ?? '',
|
||||||
|
createdAt: map['createdAt'] ?? '',
|
||||||
|
modifiedAt: map['modifiedAt'] ?? '',
|
||||||
|
isFavorite: map['isFavorite'] ?? false,
|
||||||
|
description: map['description'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String toJson() => json.encode(toMap());
|
||||||
|
|
||||||
|
factory ImmichAsset.fromJson(String source) => ImmichAsset.fromMap(json.decode(source));
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'ImmichAsset(id: $id, deviceAssetId: $deviceAssetId, userId: $userId, deviceId: $deviceId, assetType: $assetType, localPath: $localPath, remotePath: $remotePath, createdAt: $createdAt, modifiedAt: $modifiedAt, isFavorite: $isFavorite, description: $description)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
|
return other is ImmichAsset &&
|
||||||
|
other.id == id &&
|
||||||
|
other.deviceAssetId == deviceAssetId &&
|
||||||
|
other.userId == userId &&
|
||||||
|
other.deviceId == deviceId &&
|
||||||
|
other.assetType == assetType &&
|
||||||
|
other.localPath == localPath &&
|
||||||
|
other.remotePath == remotePath &&
|
||||||
|
other.createdAt == createdAt &&
|
||||||
|
other.modifiedAt == modifiedAt &&
|
||||||
|
other.isFavorite == isFavorite &&
|
||||||
|
other.description == description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return id.hashCode ^
|
||||||
|
deviceAssetId.hashCode ^
|
||||||
|
userId.hashCode ^
|
||||||
|
deviceId.hashCode ^
|
||||||
|
assetType.hashCode ^
|
||||||
|
localPath.hashCode ^
|
||||||
|
remotePath.hashCode ^
|
||||||
|
createdAt.hashCode ^
|
||||||
|
modifiedAt.hashCode ^
|
||||||
|
isFavorite.hashCode ^
|
||||||
|
description.hashCode;
|
||||||
|
}
|
||||||
|
}
|
98
mobile/lib/shared/models/server_info.model.dart
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
class ServerInfo {
|
||||||
|
final String diskSize;
|
||||||
|
final String diskUse;
|
||||||
|
final String diskAvailable;
|
||||||
|
final int diskSizeRaw;
|
||||||
|
final int diskUseRaw;
|
||||||
|
final int diskAvailableRaw;
|
||||||
|
final double diskUsagePercentage;
|
||||||
|
ServerInfo({
|
||||||
|
required this.diskSize,
|
||||||
|
required this.diskUse,
|
||||||
|
required this.diskAvailable,
|
||||||
|
required this.diskSizeRaw,
|
||||||
|
required this.diskUseRaw,
|
||||||
|
required this.diskAvailableRaw,
|
||||||
|
required this.diskUsagePercentage,
|
||||||
|
});
|
||||||
|
|
||||||
|
ServerInfo copyWith({
|
||||||
|
String? diskSize,
|
||||||
|
String? diskUse,
|
||||||
|
String? diskAvailable,
|
||||||
|
int? diskSizeRaw,
|
||||||
|
int? diskUseRaw,
|
||||||
|
int? diskAvailableRaw,
|
||||||
|
double? diskUsagePercentage,
|
||||||
|
}) {
|
||||||
|
return ServerInfo(
|
||||||
|
diskSize: diskSize ?? this.diskSize,
|
||||||
|
diskUse: diskUse ?? this.diskUse,
|
||||||
|
diskAvailable: diskAvailable ?? this.diskAvailable,
|
||||||
|
diskSizeRaw: diskSizeRaw ?? this.diskSizeRaw,
|
||||||
|
diskUseRaw: diskUseRaw ?? this.diskUseRaw,
|
||||||
|
diskAvailableRaw: diskAvailableRaw ?? this.diskAvailableRaw,
|
||||||
|
diskUsagePercentage: diskUsagePercentage ?? this.diskUsagePercentage,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'diskSize': diskSize,
|
||||||
|
'diskUse': diskUse,
|
||||||
|
'diskAvailable': diskAvailable,
|
||||||
|
'diskSizeRaw': diskSizeRaw,
|
||||||
|
'diskUseRaw': diskUseRaw,
|
||||||
|
'diskAvailableRaw': diskAvailableRaw,
|
||||||
|
'diskUsagePercentage': diskUsagePercentage,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory ServerInfo.fromMap(Map<String, dynamic> map) {
|
||||||
|
return ServerInfo(
|
||||||
|
diskSize: map['diskSize'] ?? '',
|
||||||
|
diskUse: map['diskUse'] ?? '',
|
||||||
|
diskAvailable: map['diskAvailable'] ?? '',
|
||||||
|
diskSizeRaw: map['diskSizeRaw']?.toInt() ?? 0,
|
||||||
|
diskUseRaw: map['diskUseRaw']?.toInt() ?? 0,
|
||||||
|
diskAvailableRaw: map['diskAvailableRaw']?.toInt() ?? 0,
|
||||||
|
diskUsagePercentage: map['diskUsagePercentage']?.toDouble() ?? 0.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String toJson() => json.encode(toMap());
|
||||||
|
|
||||||
|
factory ServerInfo.fromJson(String source) => ServerInfo.fromMap(json.decode(source));
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'ServerInfo(diskSize: $diskSize, diskUse: $diskUse, diskAvailable: $diskAvailable, diskSizeRaw: $diskSizeRaw, diskUseRaw: $diskUseRaw, diskAvailableRaw: $diskAvailableRaw, diskUsagePercentage: $diskUsagePercentage)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
|
return other is ServerInfo &&
|
||||||
|
other.diskSize == diskSize &&
|
||||||
|
other.diskUse == diskUse &&
|
||||||
|
other.diskAvailable == diskAvailable &&
|
||||||
|
other.diskSizeRaw == diskSizeRaw &&
|
||||||
|
other.diskUseRaw == diskUseRaw &&
|
||||||
|
other.diskAvailableRaw == diskAvailableRaw &&
|
||||||
|
other.diskUsagePercentage == diskUsagePercentage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return diskSize.hashCode ^
|
||||||
|
diskUse.hashCode ^
|
||||||
|
diskAvailable.hashCode ^
|
||||||
|
diskSizeRaw.hashCode ^
|
||||||
|
diskUseRaw.hashCode ^
|
||||||
|
diskAvailableRaw.hashCode ^
|
||||||
|
diskUsagePercentage.hashCode;
|
||||||
|
}
|
||||||
|
}
|
13
mobile/lib/shared/providers/app_state.provider.dart
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
|
||||||
|
enum AppStateEnum {
|
||||||
|
active,
|
||||||
|
inactive,
|
||||||
|
paused,
|
||||||
|
resumed,
|
||||||
|
detached,
|
||||||
|
}
|
||||||
|
|
||||||
|
final appStateProvider = StateProvider<AppStateEnum>((ref) {
|
||||||
|
return AppStateEnum.active;
|
||||||
|
});
|
137
mobile/lib/shared/providers/backup.provider.dart
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/shared/services/server_info.service.dart';
|
||||||
|
import 'package:immich_mobile/shared/models/backup_state.model.dart';
|
||||||
|
import 'package:immich_mobile/shared/models/server_info.model.dart';
|
||||||
|
import 'package:immich_mobile/shared/services/backup.service.dart';
|
||||||
|
import 'package:photo_manager/photo_manager.dart';
|
||||||
|
|
||||||
|
class BackupNotifier extends StateNotifier<BackUpState> {
|
||||||
|
BackupNotifier()
|
||||||
|
: super(
|
||||||
|
BackUpState(
|
||||||
|
backupProgress: BackUpProgressEnum.idle,
|
||||||
|
backingUpAssetCount: 0,
|
||||||
|
assetOnDatabase: 0,
|
||||||
|
totalAssetCount: 0,
|
||||||
|
progressInPercentage: 0,
|
||||||
|
cancelToken: CancelToken(),
|
||||||
|
serverInfo: ServerInfo(
|
||||||
|
diskAvailable: "0",
|
||||||
|
diskAvailableRaw: 0,
|
||||||
|
diskSize: "0",
|
||||||
|
diskSizeRaw: 0,
|
||||||
|
diskUsagePercentage: 0.0,
|
||||||
|
diskUse: "0",
|
||||||
|
diskUseRaw: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final BackupService _backupService = BackupService();
|
||||||
|
final ServerInfoService _serverInfoService = ServerInfoService();
|
||||||
|
|
||||||
|
void getBackupInfo() async {
|
||||||
|
_updateServerInfo();
|
||||||
|
|
||||||
|
List<AssetPathEntity> list = await PhotoManager.getAssetPathList(onlyAll: true, type: RequestType.image);
|
||||||
|
|
||||||
|
if (list.isEmpty) {
|
||||||
|
debugPrint("No Asset On Device");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int totalAsset = list[0].assetCount;
|
||||||
|
List<String> didBackupAsset = await _backupService.getDeviceBackupAsset();
|
||||||
|
|
||||||
|
state = state.copyWith(totalAssetCount: totalAsset, assetOnDatabase: didBackupAsset.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void startBackupProcess() async {
|
||||||
|
_updateServerInfo();
|
||||||
|
|
||||||
|
state = state.copyWith(backupProgress: BackUpProgressEnum.inProgress);
|
||||||
|
|
||||||
|
var authResult = await PhotoManager.requestPermissionExtend();
|
||||||
|
if (authResult.isAuth) {
|
||||||
|
await PhotoManager.clearFileCache();
|
||||||
|
// await PhotoManager.presentLimited();
|
||||||
|
// Gather assets info
|
||||||
|
List<AssetPathEntity> list =
|
||||||
|
await PhotoManager.getAssetPathList(hasAll: true, onlyAll: true, type: RequestType.image);
|
||||||
|
|
||||||
|
if (list.isEmpty) {
|
||||||
|
debugPrint("No Asset On Device - Abort Backup Process");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int totalAsset = list[0].assetCount;
|
||||||
|
List<AssetEntity> currentAssets = await list[0].getAssetListRange(start: 0, end: totalAsset);
|
||||||
|
|
||||||
|
// Get device assets info from database
|
||||||
|
// Compare and find different assets that has not been backing up
|
||||||
|
// Backup those assets
|
||||||
|
List<String> backupAsset = await _backupService.getDeviceBackupAsset();
|
||||||
|
|
||||||
|
state = state.copyWith(totalAssetCount: totalAsset, assetOnDatabase: backupAsset.length);
|
||||||
|
// Remove item that has already been backed up
|
||||||
|
for (var backupAssetId in backupAsset) {
|
||||||
|
currentAssets.removeWhere((e) => e.id == backupAssetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentAssets.isEmpty) {
|
||||||
|
state = state.copyWith(backupProgress: BackUpProgressEnum.idle);
|
||||||
|
}
|
||||||
|
|
||||||
|
state = state.copyWith(backingUpAssetCount: currentAssets.length);
|
||||||
|
|
||||||
|
// Perform Packup
|
||||||
|
state = state.copyWith(cancelToken: CancelToken());
|
||||||
|
_backupService.backupAsset(currentAssets, state.cancelToken, _onAssetUploaded, _onUploadProgress);
|
||||||
|
} else {
|
||||||
|
PhotoManager.openSetting();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cancelBackup() {
|
||||||
|
state.cancelToken.cancel('Cancel Backup');
|
||||||
|
state = state.copyWith(backupProgress: BackUpProgressEnum.idle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onAssetUploaded() {
|
||||||
|
state =
|
||||||
|
state.copyWith(backingUpAssetCount: state.backingUpAssetCount - 1, assetOnDatabase: state.assetOnDatabase + 1);
|
||||||
|
|
||||||
|
if (state.backingUpAssetCount == 0) {
|
||||||
|
state = state.copyWith(backupProgress: BackUpProgressEnum.done, progressInPercentage: 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateServerInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onUploadProgress(int sent, int total) {
|
||||||
|
state = state.copyWith(progressInPercentage: (sent.toDouble() / total.toDouble() * 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateServerInfo() async {
|
||||||
|
var serverInfo = await _serverInfoService.getServerInfo();
|
||||||
|
|
||||||
|
// Update server info
|
||||||
|
state = state.copyWith(
|
||||||
|
serverInfo: ServerInfo(
|
||||||
|
diskSize: serverInfo.diskSize,
|
||||||
|
diskUse: serverInfo.diskUse,
|
||||||
|
diskAvailable: serverInfo.diskAvailable,
|
||||||
|
diskSizeRaw: serverInfo.diskSizeRaw,
|
||||||
|
diskUseRaw: serverInfo.diskUseRaw,
|
||||||
|
diskAvailableRaw: serverInfo.diskAvailableRaw,
|
||||||
|
diskUsagePercentage: serverInfo.diskUsagePercentage,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final backupProvider = StateNotifierProvider<BackupNotifier, BackUpState>((ref) {
|
||||||
|
return BackupNotifier();
|
||||||
|
});
|
124
mobile/lib/shared/services/backup.service.dart
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:immich_mobile/constants/hive_box.dart';
|
||||||
|
import 'package:immich_mobile/shared/services/network.service.dart';
|
||||||
|
import 'package:immich_mobile/shared/models/device_info.model.dart';
|
||||||
|
import 'package:immich_mobile/utils/dio_http_interceptor.dart';
|
||||||
|
import 'package:immich_mobile/utils/files_helper.dart';
|
||||||
|
import 'package:photo_manager/photo_manager.dart';
|
||||||
|
import 'package:http_parser/http_parser.dart';
|
||||||
|
import 'package:path/path.dart' as p;
|
||||||
|
import 'package:exif/exif.dart';
|
||||||
|
|
||||||
|
class BackupService {
|
||||||
|
final NetworkService _networkService = NetworkService();
|
||||||
|
|
||||||
|
Future<List<String>> getDeviceBackupAsset() async {
|
||||||
|
String deviceId = Hive.box(userInfoBox).get(deviceIdKey);
|
||||||
|
|
||||||
|
Response response = await _networkService.getRequest(url: "asset/$deviceId");
|
||||||
|
List<dynamic> result = jsonDecode(response.toString());
|
||||||
|
|
||||||
|
return result.cast<String>();
|
||||||
|
}
|
||||||
|
|
||||||
|
backupAsset(List<AssetEntity> assetList, CancelToken cancelToken, Function singleAssetDoneCb,
|
||||||
|
Function(int, int) uploadProgress) async {
|
||||||
|
var dio = Dio();
|
||||||
|
dio.interceptors.add(AuthenticatedRequestInterceptor());
|
||||||
|
String deviceId = Hive.box(userInfoBox).get(deviceIdKey);
|
||||||
|
String savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
|
||||||
|
File? file;
|
||||||
|
|
||||||
|
for (var entity in assetList) {
|
||||||
|
try {
|
||||||
|
file = await entity.file.timeout(const Duration(seconds: 5));
|
||||||
|
|
||||||
|
if (file != null) {
|
||||||
|
// reading exif
|
||||||
|
// var exifData = await readExifFromFile(file);
|
||||||
|
|
||||||
|
// for (String key in exifData.keys) {
|
||||||
|
// debugPrint("- $key (${exifData[key]?.tagType}): ${exifData[key]}");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// debugPrint("------------------");
|
||||||
|
String originalFileName = await entity.titleAsync;
|
||||||
|
String fileNameWithoutPath = originalFileName.toString().split(".")[0];
|
||||||
|
var fileExtension = p.extension(file.path);
|
||||||
|
LatLng coordinate = await entity.latlngAsync();
|
||||||
|
var mimeType = FileHelper.getMimeType(file.path);
|
||||||
|
var formData = FormData.fromMap({
|
||||||
|
'deviceAssetId': entity.id,
|
||||||
|
'deviceId': deviceId,
|
||||||
|
'assetType': _getAssetType(entity.type),
|
||||||
|
'createdAt': entity.createDateTime.toIso8601String(),
|
||||||
|
'modifiedAt': entity.modifiedDateTime.toIso8601String(),
|
||||||
|
'isFavorite': entity.isFavorite,
|
||||||
|
'fileExtension': fileExtension,
|
||||||
|
'lat': coordinate.latitude,
|
||||||
|
'lon': coordinate.longitude,
|
||||||
|
'files': [
|
||||||
|
await MultipartFile.fromFile(
|
||||||
|
file.path,
|
||||||
|
filename: fileNameWithoutPath,
|
||||||
|
contentType: MediaType(
|
||||||
|
mimeType["type"],
|
||||||
|
mimeType["subType"],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
Response res = await dio.post(
|
||||||
|
'$savedEndpoint/asset/upload',
|
||||||
|
data: formData,
|
||||||
|
cancelToken: cancelToken,
|
||||||
|
onSendProgress: (sent, total) => uploadProgress(sent, total),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res.statusCode == 201) {
|
||||||
|
singleAssetDoneCb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} on DioError catch (e) {
|
||||||
|
debugPrint("DioError backupAsset: ${e.response}");
|
||||||
|
break;
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint("ERROR backupAsset: ${e.toString()}");
|
||||||
|
continue;
|
||||||
|
} finally {
|
||||||
|
if (Platform.isIOS) {
|
||||||
|
file?.deleteSync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getAssetType(AssetType assetType) {
|
||||||
|
switch (assetType) {
|
||||||
|
case AssetType.audio:
|
||||||
|
return "AUDIO";
|
||||||
|
case AssetType.image:
|
||||||
|
return "IMAGE";
|
||||||
|
case AssetType.video:
|
||||||
|
return "VIDEO";
|
||||||
|
case AssetType.other:
|
||||||
|
return "OTHER";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<DeviceInfoRemote> setAutoBackup(bool status, String deviceId, String deviceType) async {
|
||||||
|
var res = await _networkService.patchRequest(url: 'device-info', data: {
|
||||||
|
"isAutoBackup": status,
|
||||||
|
"deviceId": deviceId,
|
||||||
|
"deviceType": deviceType,
|
||||||
|
});
|
||||||
|
|
||||||
|
return DeviceInfoRemote.fromJson(res.toString());
|
||||||
|
}
|
||||||
|
}
|
30
mobile/lib/shared/services/device_info.service.dart
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class DeviceInfoService {
|
||||||
|
Future<Map<String, dynamic>> getDeviceInfo() async {
|
||||||
|
// Get device info
|
||||||
|
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
||||||
|
String? deviceId = "";
|
||||||
|
String deviceType = "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
||||||
|
deviceId = androidInfo.androidId;
|
||||||
|
deviceType = "ANDROID";
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint("Not an android device");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
|
||||||
|
deviceId = iosInfo.identifierForVendor;
|
||||||
|
deviceType = "IOS";
|
||||||
|
debugPrint("Device ID: $deviceId");
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint("Not an ios device");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {"deviceId": deviceId, "deviceType": deviceType};
|
||||||
|
}
|
||||||
|
}
|
18
mobile/lib/shared/services/local_storage.service.dart
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:immich_mobile/constants/hive_box.dart';
|
||||||
|
|
||||||
|
class LocalStorageService {
|
||||||
|
late Box _box;
|
||||||
|
|
||||||
|
LocalStorageService() {
|
||||||
|
_box = Hive.box(userInfoBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
T get<T>(String key) {
|
||||||
|
return _box.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
put<T>(String key, T value) {
|
||||||
|
return _box.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
89
mobile/lib/shared/services/network.service.dart
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:immich_mobile/constants/hive_box.dart';
|
||||||
|
import 'package:immich_mobile/utils/dio_http_interceptor.dart';
|
||||||
|
|
||||||
|
class NetworkService {
|
||||||
|
Future<dynamic> getRequest({required String url}) async {
|
||||||
|
try {
|
||||||
|
var dio = Dio();
|
||||||
|
dio.interceptors.add(AuthenticatedRequestInterceptor());
|
||||||
|
|
||||||
|
var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
|
||||||
|
Response res = await dio.get('$savedEndpoint/$url');
|
||||||
|
|
||||||
|
if (res.statusCode == 200) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
} on DioError catch (e) {
|
||||||
|
debugPrint("DioError: ${e.response}");
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint("ERROR getRequest: ${e.toString()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<dynamic> postRequest({required String url, dynamic data}) async {
|
||||||
|
try {
|
||||||
|
var dio = Dio();
|
||||||
|
dio.interceptors.add(AuthenticatedRequestInterceptor());
|
||||||
|
|
||||||
|
var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
|
||||||
|
String validUrl = Uri.parse('$savedEndpoint/$url').toString();
|
||||||
|
Response res = await dio.post(validUrl, data: data);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
} on DioError catch (e) {
|
||||||
|
debugPrint("DioError: ${e.response}");
|
||||||
|
return false;
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint("ERROR BackupService: $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<dynamic> patchRequest({required String url, dynamic data}) async {
|
||||||
|
try {
|
||||||
|
var dio = Dio();
|
||||||
|
dio.interceptors.add(AuthenticatedRequestInterceptor());
|
||||||
|
|
||||||
|
var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
|
||||||
|
|
||||||
|
String validUrl = Uri.parse('$savedEndpoint/$url').toString();
|
||||||
|
Response res = await dio.patch(validUrl, data: data);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
} on DioError catch (e) {
|
||||||
|
debugPrint("DioError: ${e.response}");
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint("ERROR BackupService: $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> pingServer() async {
|
||||||
|
try {
|
||||||
|
var dio = Dio();
|
||||||
|
|
||||||
|
var savedEndpoint = Hive.box(userInfoBox).get(serverEndpointKey);
|
||||||
|
|
||||||
|
String validUrl = Uri.parse('$savedEndpoint/server-info/ping').toString();
|
||||||
|
|
||||||
|
debugPrint("pint server at url $validUrl");
|
||||||
|
Response res = await dio.get(validUrl);
|
||||||
|
var jsonRespsonse = jsonDecode(res.toString());
|
||||||
|
|
||||||
|
if (jsonRespsonse["res"] == "pong") {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} on DioError catch (e) {
|
||||||
|
debugPrint("[PING SERVER] DioError: ${e.response} - $e");
|
||||||
|
return false;
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint("ERROR BackupService: $e");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|