Resolved merge conflicts
|
@ -1,107 +1,108 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="io.ente.photos">
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="io.ente.photos">
|
||||
<application android:name="${applicationName}"
|
||||
android:label="@string/app_name"
|
||||
android:icon="@mipmap/launcher_icon"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:allowBackup="false"
|
||||
android:fullBackupContent="false"
|
||||
android:largeHeap="true">
|
||||
android:label="@string/app_name"
|
||||
android:icon="@mipmap/launcher_icon"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:allowBackup="false"
|
||||
android:fullBackupContent="false"
|
||||
android:largeHeap="true">
|
||||
|
||||
<activity android:name=".MainActivity" android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:exported="true"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
<!-- <intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="image/*" />
|
||||
<data android:mimeType="video/*" />
|
||||
</intent-filter> -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.PICK" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="image/*" />
|
||||
<data android:mimeType="video/*" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.GET_CONTENT" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.OPENABLE" />
|
||||
<data android:mimeType="image/*" />
|
||||
<data android:mimeType="video/*" />
|
||||
</intent-filter>
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:exported="true"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="image/*" />
|
||||
<data android:mimeType="video/*" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.PICK" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="image/*" />
|
||||
<data android:mimeType="video/*" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.GET_CONTENT" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.OPENABLE" />
|
||||
<data android:mimeType="image/*" />
|
||||
<data android:mimeType="video/*" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- file provider to share files having a file:// URI -->
|
||||
<!-- file provider to share files having a file:// URI -->
|
||||
|
||||
<!--Filter to support sharing images into our app-->
|
||||
<!--Filter
|
||||
to support sharing images into our app-->
|
||||
<intent-filter android:label="@string/backup">
|
||||
<action android:name="android.intent.action.SEND"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:mimeType="image/*"/>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="image/*" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:label="@string/backup">
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:mimeType="image/*"/>
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="image/*" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:label="@string/backup">
|
||||
<action android:name="android.intent.action.SEND"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:mimeType="video/*"/>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="video/*" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:label="@string/backup">
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:mimeType="video/*"/>
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="video/*" />
|
||||
</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"/>
|
||||
<meta-data android:name="flutterEmbedding" android:value="2" />
|
||||
<meta-data android:name="asset_statements"
|
||||
android:resource="@string/asset_statements"/>
|
||||
android:resource="@string/asset_statements" />
|
||||
<meta-data android:name="io.sentry.dsn"
|
||||
android:value="https://2235e5c99219488ea93da34b9ac1cb68@sentry.ente.io/4"/>
|
||||
android:value="https://2235e5c99219488ea93da34b9ac1cb68@sentry.ente.io/4" />
|
||||
<meta-data android:name="firebase_analytics_collection_deactivated"
|
||||
android:value="true"/>
|
||||
android:value="true" />
|
||||
</application>
|
||||
|
||||
<!-- Android 11: https://developer.android.com/preview/privacy/package-visibility -->
|
||||
<!-- https://developer.android.com/training/package-visibility/use-cases -->
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.SENDTO"/>
|
||||
<data android:scheme="mailto"/>
|
||||
<action android:name="android.intent.action.SENDTO" />
|
||||
<data android:scheme="mailto" />
|
||||
</intent>
|
||||
</queries>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.MANAGE_MEDIA"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.MANAGE_MEDIA" />
|
||||
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_MEDIA_IMAGES"/> <!-- If you want to read images-->
|
||||
android:name="android.permission.READ_MEDIA_IMAGES" /> <!-- If you want to read images-->
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_MEDIA_VIDEO"/> <!-- If you want to read videos-->
|
||||
android:name="android.permission.READ_MEDIA_VIDEO" /> <!-- If you want to read videos-->
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="32"/>
|
||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="32" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="29"
|
||||
tools:ignore="ScopedStorage"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="com.android.vending.BILLING"/>
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="29"
|
||||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="com.android.vending.BILLING" />
|
||||
</manifest>
|
BIN
android/app/src/main/res/drawable-hdpi/android12splash.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 6.3 KiB |
BIN
android/app/src/main/res/drawable-mdpi/android12splash.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.4 KiB |
BIN
android/app/src/main/res/drawable-night-hdpi/android12splash.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 6.4 KiB |
BIN
android/app/src/main/res/drawable-night-mdpi/android12splash.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 6 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 6 KiB |
After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
BIN
android/app/src/main/res/drawable-xhdpi/android12splash.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 6.1 KiB |
BIN
android/app/src/main/res/drawable-xxhdpi/android12splash.png
Normal file
After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
BIN
android/app/src/main/res/drawable-xxxhdpi/android12splash.png
Normal file
After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
|
@ -2,4 +2,5 @@
|
|||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<item name="android:forceDarkAllowed">false</item>
|
||||
<item name="android:windowFullscreen">false</item>
|
||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||
<item name="android:windowSplashScreenBackground">#000000</item>
|
||||
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</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
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
<item name="android:forceDarkAllowed">false</item>
|
||||
<item name="android:windowFullscreen">false</item>
|
||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</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
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<item name="android:forceDarkAllowed">false</item>
|
||||
<item name="android:windowFullscreen">false</item>
|
||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||
<item name="android:windowSplashScreenBackground">#ffffff</item>
|
||||
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</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
|
||||
|
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 15 KiB |
|
@ -3,9 +3,9 @@ PODS:
|
|||
- Flutter
|
||||
- camera_avfoundation (0.0.1):
|
||||
- Flutter
|
||||
- connectivity (0.0.1):
|
||||
- connectivity_plus (0.0.1):
|
||||
- Flutter
|
||||
- Reachability
|
||||
- ReachabilitySwift
|
||||
- device_info (0.0.1):
|
||||
- Flutter
|
||||
- Firebase/CoreOnly (10.3.0):
|
||||
|
@ -136,7 +136,7 @@ PODS:
|
|||
- Flutter
|
||||
- FlutterMacOS
|
||||
- PromisesObjC (2.1.1)
|
||||
- Reachability (3.2)
|
||||
- ReachabilitySwift (5.0.0)
|
||||
- receive_sharing_intent (0.0.1):
|
||||
- Flutter
|
||||
- SDWebImage (5.15.2):
|
||||
|
@ -178,7 +178,7 @@ PODS:
|
|||
DEPENDENCIES:
|
||||
- background_fetch (from `.symlinks/plugins/background_fetch/ios`)
|
||||
- camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`)
|
||||
- connectivity (from `.symlinks/plugins/connectivity/ios`)
|
||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||
- device_info (from `.symlinks/plugins/device_info/ios`)
|
||||
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
||||
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
|
||||
|
@ -231,7 +231,7 @@ SPEC REPOS:
|
|||
- nanopb
|
||||
- OrderedSet
|
||||
- PromisesObjC
|
||||
- Reachability
|
||||
- ReachabilitySwift
|
||||
- SDWebImage
|
||||
- SDWebImageWebPCoder
|
||||
- Sentry
|
||||
|
@ -242,8 +242,8 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/background_fetch/ios"
|
||||
camera_avfoundation:
|
||||
:path: ".symlinks/plugins/camera_avfoundation/ios"
|
||||
connectivity:
|
||||
:path: ".symlinks/plugins/connectivity/ios"
|
||||
connectivity_plus:
|
||||
:path: ".symlinks/plugins/connectivity_plus/ios"
|
||||
device_info:
|
||||
:path: ".symlinks/plugins/device_info/ios"
|
||||
firebase_core:
|
||||
|
@ -320,7 +320,7 @@ EXTERNAL SOURCES:
|
|||
SPEC CHECKSUMS:
|
||||
background_fetch: bd64e544b303ee4cd4cf2fe8cb2187b72aecf9ca
|
||||
camera_avfoundation: 07c77549ea54ad95d8581be86617c094a46280d9
|
||||
connectivity: c4130b2985d4ef6fd26f9702e886bd5260681467
|
||||
connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e
|
||||
device_info: d7d233b645a32c40dfdc212de5cf646ca482f175
|
||||
Firebase: f92fc551ead69c94168d36c2b26188263860acd9
|
||||
firebase_core: f95c8bbe65213d406d592573ad42a12d64849cb8
|
||||
|
@ -358,7 +358,7 @@ SPEC CHECKSUMS:
|
|||
path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852
|
||||
photo_manager: 4f6810b7dfc4feb03b461ac1a70dacf91fba7604
|
||||
PromisesObjC: ab77feca74fa2823e7af4249b8326368e61014cb
|
||||
Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96
|
||||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||
receive_sharing_intent: c0d87310754e74c0f9542947e7cbdf3a0335a3b1
|
||||
SDWebImage: 8ab87d4b3e5cc4927bd47f78db6ceb0b94442577
|
||||
SDWebImageWebPCoder: 4851414d9f8894e328e8b97c93ea4f4f4e4418ae
|
||||
|
|
|
@ -268,14 +268,14 @@
|
|||
"${BUILT_PRODUCTS_DIR}/Mantle/Mantle.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/OrderedSet/OrderedSet.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/Reachability/Reachability.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/ReachabilitySwift/Reachability.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/SDWebImageWebPCoder/SDWebImageWebPCoder.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/Sentry/Sentry.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/Toast/Toast.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/background_fetch/background_fetch.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/camera_avfoundation/camera_avfoundation.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/connectivity/connectivity.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/connectivity_plus/connectivity_plus.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/device_info/device_info.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/fk_user_agent/fk_user_agent.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/flutter_email_sender/flutter_email_sender.framework",
|
||||
|
@ -331,7 +331,7 @@
|
|||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Toast.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/background_fetch.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/camera_avfoundation.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity_plus.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/fk_user_agent.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_email_sender.framework",
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
"images" : [
|
||||
{
|
||||
"filename" : "background.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
|
@ -13,36 +12,7 @@
|
|||
}
|
||||
],
|
||||
"filename" : "darkbackground.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 6 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
|
@ -38,7 +38,7 @@
|
|||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="LaunchImage" width="1024" height="1024"/>
|
||||
<image name="LaunchImage" width="1152" height="1152"/>
|
||||
<image name="LaunchBackground" width="1" height="1"/>
|
||||
</resources>
|
||||
</document>
|
|
@ -23,19 +23,19 @@
|
|||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>12.0</string>
|
||||
<string>12.0</string>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>googlegmail</string>
|
||||
<string>x-dispatch</string>
|
||||
<string>readdle-spark</string>
|
||||
<string>airmail</string>
|
||||
<string>ms-outlook</string>
|
||||
<string>ymail</string>
|
||||
<string>fastmail</string>
|
||||
<string>superhuman</string>
|
||||
<string>protonmail</string>
|
||||
</array>
|
||||
<array>
|
||||
<string>googlegmail</string>
|
||||
<string>x-dispatch</string>
|
||||
<string>readdle-spark</string>
|
||||
<string>airmail</string>
|
||||
<string>ms-outlook</string>
|
||||
<string>ymail</string>
|
||||
<string>fastmail</string>
|
||||
<string>superhuman</string>
|
||||
<string>protonmail</string>
|
||||
</array>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
|
@ -67,8 +67,7 @@
|
|||
<key>NSFaceIDUsageDescription</key>
|
||||
<string>Please allow ente to lock itself with FaceID or TouchID</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Please allow access to your camera so that you can take photos
|
||||
within ente</string>
|
||||
<string>Please allow access to your camera so that you can take photos within ente</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Please allow access to your photos so that ente can encrypt and back them up.</string>
|
||||
<key>UIBackgroundModes</key>
|
||||
|
@ -102,5 +101,5 @@
|
|||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
71
lib/app.dart
|
@ -4,16 +4,16 @@ import 'package:adaptive_theme/adaptive_theme.dart';
|
|||
import 'package:background_fetch/background_fetch.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:media_extension/media_extension.dart';
|
||||
import 'package:media_extension/media_extension_action_types.dart';
|
||||
import 'package:photos/ente_theme_data.dart';
|
||||
import 'package:photos/services/app_lifecycle_service.dart';
|
||||
import 'package:photos/services/sync_service.dart';
|
||||
import 'package:photos/ui/home_widget.dart';
|
||||
import "package:photos/ui/viewer/actions/file_viewer.dart";
|
||||
import "package:photos/utils/intent_util.dart";
|
||||
|
||||
class EnteApp extends StatefulWidget {
|
||||
final Future<void> Function(String) runBackgroundTask;
|
||||
|
@ -31,63 +31,46 @@ class EnteApp extends StatefulWidget {
|
|||
|
||||
class _EnteAppState extends State<EnteApp> with WidgetsBindingObserver {
|
||||
final _logger = Logger("EnteAppState");
|
||||
final _mediaExtensionPlugin = MediaExtension();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_logger.info('init App');
|
||||
super.initState();
|
||||
setupIntentAction();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
}
|
||||
|
||||
Future<bool> initIntentAction() async {
|
||||
if (!Platform.isAndroid) {
|
||||
AppLifecycleService.instance.setIntentAction(IntentAction.main);
|
||||
return true;
|
||||
}
|
||||
IntentAction intentAction = IntentAction.main;
|
||||
try {
|
||||
final actionResult = await _mediaExtensionPlugin.getIntentAction();
|
||||
intentAction = actionResult.action!;
|
||||
} on PlatformException {
|
||||
intentAction = IntentAction.main;
|
||||
} catch (error) {
|
||||
_logger.info(error);
|
||||
intentAction = IntentAction.main;
|
||||
}
|
||||
if (intentAction == IntentAction.main) {
|
||||
void setupIntentAction() async {
|
||||
final mediaExtentionAction = Platform.isAndroid
|
||||
? await initIntentAction()
|
||||
: MediaExtentionAction(action: IntentAction.main);
|
||||
AppLifecycleService.instance.setMediaExtensionAction(mediaExtentionAction);
|
||||
if (mediaExtentionAction.action == IntentAction.main) {
|
||||
_configureBackgroundFetch();
|
||||
}
|
||||
AppLifecycleService.instance.setIntentAction(intentAction);
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (Platform.isAndroid || kDebugMode) {
|
||||
return FutureBuilder(
|
||||
future: initIntentAction(),
|
||||
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
|
||||
return snapshot.data != null && snapshot.data == true
|
||||
? AdaptiveTheme(
|
||||
light: lightThemeData,
|
||||
dark: darkThemeData,
|
||||
initial: AdaptiveThemeMode.system,
|
||||
builder: (lightTheme, dartTheme) => MaterialApp(
|
||||
title: "ente",
|
||||
themeMode: ThemeMode.system,
|
||||
theme: lightTheme,
|
||||
darkTheme: dartTheme,
|
||||
home: const HomeWidget(),
|
||||
debugShowCheckedModeBanner: false,
|
||||
builder: EasyLoading.init(),
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
localizationsDelegates:
|
||||
AppLocalizations.localizationsDelegates,
|
||||
),
|
||||
)
|
||||
: Container();
|
||||
},
|
||||
return AdaptiveTheme(
|
||||
light: lightThemeData,
|
||||
dark: darkThemeData,
|
||||
initial: AdaptiveThemeMode.system,
|
||||
builder: (lightTheme, dartTheme) => MaterialApp(
|
||||
title: "ente",
|
||||
themeMode: ThemeMode.system,
|
||||
theme: lightTheme,
|
||||
darkTheme: dartTheme,
|
||||
home: AppLifecycleService.instance.mediaExtensionAction.action ==
|
||||
IntentAction.view
|
||||
? const FileViewer()
|
||||
: const HomeWidget(),
|
||||
debugShowCheckedModeBanner: false,
|
||||
builder: EasyLoading.init(),
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return MaterialApp(
|
||||
|
|
|
@ -1091,13 +1091,17 @@ class FilesDB {
|
|||
return count ?? 0;
|
||||
}
|
||||
|
||||
Future<int> fileCountWithVisibility(int visibility, int ownerID) async {
|
||||
Future<int> archivedFilesCount(
|
||||
int visibility,
|
||||
int ownerID,
|
||||
Set<int> hiddenCollections,
|
||||
) async {
|
||||
final db = await instance.database;
|
||||
final count = Sqflite.firstIntValue(
|
||||
await db.rawQuery(
|
||||
'SELECT COUNT(distinct($columnUploadedFileID)) FROM $filesTable where '
|
||||
'$columnMMdVisibility'
|
||||
' = $visibility AND $columnOwnerID = $ownerID',
|
||||
' = $visibility AND $columnOwnerID = $ownerID AND $columnCollectionID NOT IN (${hiddenCollections.join(', ')})',
|
||||
),
|
||||
);
|
||||
return count ?? 0;
|
||||
|
|
|
@ -3,7 +3,9 @@ import "package:flutter/services.dart";
|
|||
class UpperCaseTextFormatter extends TextInputFormatter {
|
||||
@override
|
||||
TextEditingValue formatEditUpdate(
|
||||
TextEditingValue oldValue, TextEditingValue newValue) {
|
||||
TextEditingValue oldValue,
|
||||
TextEditingValue newValue,
|
||||
) {
|
||||
return TextEditingValue(
|
||||
text: newValue.text.toUpperCase(),
|
||||
selection: newValue.selection,
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:background_fetch/background_fetch.dart';
|
|||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import "package:flutter/rendering.dart";
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:photos/app.dart';
|
||||
|
@ -54,6 +55,7 @@ const kFGTaskDeathTimeoutInMicroseconds = 5000000;
|
|||
const kBackgroundLockLatency = Duration(seconds: 3);
|
||||
|
||||
void main() async {
|
||||
debugRepaintRainbowEnabled = false;
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await _runInForeground();
|
||||
BackgroundFetch.registerHeadlessTask(_headlessTaskHandler);
|
||||
|
|
|
@ -5,15 +5,17 @@ class AppLifecycleService {
|
|||
final _logger = Logger("AppLifecycleService");
|
||||
|
||||
bool isForeground = false;
|
||||
IntentAction intentAction = IntentAction.main;
|
||||
MediaExtentionAction mediaExtensionAction =
|
||||
MediaExtentionAction(action: IntentAction.main);
|
||||
|
||||
static final AppLifecycleService instance =
|
||||
AppLifecycleService._privateConstructor();
|
||||
|
||||
AppLifecycleService._privateConstructor();
|
||||
|
||||
void setIntentAction(IntentAction intentAction) {
|
||||
this.intentAction = intentAction;
|
||||
void setMediaExtensionAction(MediaExtentionAction mediaExtensionAction) {
|
||||
_logger.info("App invoked via ${mediaExtensionAction.action}");
|
||||
this.mediaExtensionAction = mediaExtensionAction;
|
||||
}
|
||||
|
||||
void onAppInForeground(String reason) {
|
||||
|
|
|
@ -185,7 +185,9 @@ class CollectionsService {
|
|||
Future<List<CollectionWithThumbnail>> getArchivedCollectionWithThumb() async {
|
||||
final allCollections = await getCollectionsWithThumbnails();
|
||||
return allCollections
|
||||
.where((element) => element.collection.isArchived())
|
||||
.where(
|
||||
(c) => c.collection.isArchived() && !c.collection.isHidden(),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/constants.dart';
|
||||
import "package:photos/core/event_bus.dart";
|
||||
import 'package:photos/db/files_db.dart';
|
||||
import 'package:photos/db/memories_db.dart';
|
||||
import "package:photos/events/files_updated_event.dart";
|
||||
import 'package:photos/models/filters/important_items_filter.dart';
|
||||
import 'package:photos/models/memory.dart';
|
||||
import 'package:photos/services/collections_service.dart';
|
||||
|
@ -34,6 +36,17 @@ class MemoriesService extends ChangeNotifier {
|
|||
DateTime.now().microsecondsSinceEpoch - (7 * microSecondsInDay),
|
||||
);
|
||||
});
|
||||
Bus.instance.on<FilesUpdatedEvent>().where((event) {
|
||||
return event.type == EventType.deletedFromEverywhere;
|
||||
}).listen((event) {
|
||||
final generatedIDs = event.updatedFiles
|
||||
.where((element) => element.generatedID != null)
|
||||
.map((e) => e.generatedID!)
|
||||
.toSet();
|
||||
_cachedMemories?.removeWhere((element) {
|
||||
return generatedIDs.contains(element.file.generatedID);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void clearCache() {
|
||||
|
|
|
@ -6,7 +6,8 @@ class StorageBonusService {
|
|||
late StorageBonusGateway gateway;
|
||||
late SharedPreferences prefs;
|
||||
|
||||
final String _showStorageBonus = "showStorageBonus.showBanner";
|
||||
final int minTapCountBeforeHidingBanner = 5;
|
||||
final String _showStorageBonusTapCount = "showStorageBonus.tap_count";
|
||||
|
||||
void init(SharedPreferences preferences) {
|
||||
prefs = preferences;
|
||||
|
@ -18,12 +19,15 @@ class StorageBonusService {
|
|||
static StorageBonusService instance =
|
||||
StorageBonusService._privateConstructor();
|
||||
|
||||
// returns true if _showStorageBonusTapCount value is less than minTapCountBeforeHidingBanner
|
||||
bool shouldShowStorageBonus() {
|
||||
return prefs.getBool(_showStorageBonus) ?? true;
|
||||
final tapCount = prefs.getInt(_showStorageBonusTapCount) ?? 0;
|
||||
return tapCount <= minTapCountBeforeHidingBanner;
|
||||
}
|
||||
|
||||
void markStorageBonusAsDone() {
|
||||
prefs.setBool(_showStorageBonus, false).ignore();
|
||||
final tapCount = prefs.getInt(_showStorageBonusTapCount) ?? 0;
|
||||
prefs.setInt(_showStorageBonusTapCount, tapCount + 1).ignore();
|
||||
}
|
||||
|
||||
// getter for gateway
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:connectivity/connectivity.dart';
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
|
|
|
@ -48,6 +48,10 @@ class EnteColorScheme {
|
|||
final Color warning800;
|
||||
final Color caution500;
|
||||
|
||||
//golden colors
|
||||
final Color golden700;
|
||||
final Color golden500;
|
||||
|
||||
//other colors
|
||||
final Color tabIcon;
|
||||
final List<Color> avatarColors;
|
||||
|
@ -86,6 +90,8 @@ class EnteColorScheme {
|
|||
this.warning500 = _warning500,
|
||||
this.warning400 = _warning400,
|
||||
this.caution500 = _caution500,
|
||||
this.golden700 = _golden700,
|
||||
this.golden500 = _golden500,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -224,6 +230,9 @@ const Color _warning800 = Color(0xFFF53434);
|
|||
|
||||
const Color _caution500 = Color.fromRGBO(255, 194, 71, 1);
|
||||
|
||||
const Color _golden700 = Color(0xFFFDB816);
|
||||
const Color _golden500 = Color(0xFFFFC336);
|
||||
|
||||
const List<Color> avatarLight = [
|
||||
Color.fromRGBO(118, 84, 154, 1),
|
||||
Color.fromRGBO(223, 120, 97, 1),
|
||||
|
|
|
@ -86,6 +86,26 @@ class EnteTextTheme {
|
|||
final TextStyle brandSmall;
|
||||
final TextStyle brandMedium;
|
||||
|
||||
// textMuted variants
|
||||
final TextStyle h1Muted;
|
||||
final TextStyle h2Muted;
|
||||
final TextStyle h3Muted;
|
||||
final TextStyle largeMuted;
|
||||
final TextStyle bodyMuted;
|
||||
final TextStyle smallMuted;
|
||||
final TextStyle miniMuted;
|
||||
final TextStyle tinyMuted;
|
||||
|
||||
// textFaint variants
|
||||
final TextStyle h1Faint;
|
||||
final TextStyle h2Faint;
|
||||
final TextStyle h3Faint;
|
||||
final TextStyle largeFaint;
|
||||
final TextStyle bodyFaint;
|
||||
final TextStyle smallFaint;
|
||||
final TextStyle miniFaint;
|
||||
final TextStyle tinyFaint;
|
||||
|
||||
const EnteTextTheme({
|
||||
required this.h1,
|
||||
required this.h1Bold,
|
||||
|
@ -105,13 +125,42 @@ class EnteTextTheme {
|
|||
required this.tinyBold,
|
||||
required this.brandSmall,
|
||||
required this.brandMedium,
|
||||
required this.h1Muted,
|
||||
required this.h2Muted,
|
||||
required this.h3Muted,
|
||||
required this.largeMuted,
|
||||
required this.bodyMuted,
|
||||
required this.smallMuted,
|
||||
required this.miniMuted,
|
||||
required this.tinyMuted,
|
||||
required this.h1Faint,
|
||||
required this.h2Faint,
|
||||
required this.h3Faint,
|
||||
required this.largeFaint,
|
||||
required this.bodyFaint,
|
||||
required this.smallFaint,
|
||||
required this.miniFaint,
|
||||
required this.tinyFaint,
|
||||
});
|
||||
}
|
||||
|
||||
EnteTextTheme lightTextTheme = _buildEnteTextStyle(textBaseLight);
|
||||
EnteTextTheme darkTextTheme = _buildEnteTextStyle(textBaseDark);
|
||||
EnteTextTheme lightTextTheme = _buildEnteTextStyle(
|
||||
textBaseLight,
|
||||
textMutedLight,
|
||||
textFaintLight,
|
||||
);
|
||||
|
||||
EnteTextTheme _buildEnteTextStyle(Color color) {
|
||||
EnteTextTheme darkTextTheme = _buildEnteTextStyle(
|
||||
textBaseDark,
|
||||
textMutedDark,
|
||||
textFaintDark,
|
||||
);
|
||||
|
||||
EnteTextTheme _buildEnteTextStyle(
|
||||
Color color,
|
||||
Color textMuted,
|
||||
Color textFaint,
|
||||
) {
|
||||
return EnteTextTheme(
|
||||
h1: h1.copyWith(color: color),
|
||||
h1Bold: h1.copyWith(color: color, fontWeight: _boldWeight),
|
||||
|
@ -131,5 +180,21 @@ EnteTextTheme _buildEnteTextStyle(Color color) {
|
|||
tinyBold: tiny.copyWith(color: color, fontWeight: _boldWeight),
|
||||
brandSmall: brandStyleSmall.copyWith(color: color),
|
||||
brandMedium: brandStyleMedium.copyWith(color: color),
|
||||
h1Muted: h1.copyWith(color: textMuted),
|
||||
h2Muted: h2.copyWith(color: textMuted),
|
||||
h3Muted: h3.copyWith(color: textMuted),
|
||||
largeMuted: large.copyWith(color: textMuted),
|
||||
bodyMuted: body.copyWith(color: textMuted),
|
||||
smallMuted: small.copyWith(color: textMuted),
|
||||
miniMuted: mini.copyWith(color: textMuted),
|
||||
tinyMuted: tiny.copyWith(color: textMuted),
|
||||
h1Faint: h1.copyWith(color: textFaint),
|
||||
h2Faint: h2.copyWith(color: textFaint),
|
||||
h3Faint: h3.copyWith(color: textFaint),
|
||||
largeFaint: large.copyWith(color: textFaint),
|
||||
bodyFaint: body.copyWith(color: textFaint),
|
||||
smallFaint: small.copyWith(color: textFaint),
|
||||
miniFaint: mini.copyWith(color: textFaint),
|
||||
tinyFaint: tiny.copyWith(color: textFaint),
|
||||
);
|
||||
}
|
||||
|
|
143
lib/ui/actions/file/file_actions.dart
Normal file
|
@ -0,0 +1,143 @@
|
|||
import "package:flutter/cupertino.dart";
|
||||
import "package:modal_bottom_sheet/modal_bottom_sheet.dart";
|
||||
import "package:photos/models/file.dart";
|
||||
import "package:photos/models/file_type.dart";
|
||||
import "package:photos/theme/colors.dart";
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
import "package:photos/ui/components/action_sheet_widget.dart";
|
||||
import "package:photos/ui/components/button_widget.dart";
|
||||
import "package:photos/ui/components/models/button_type.dart";
|
||||
import "package:photos/ui/viewer/file/file_info_widget.dart";
|
||||
import "package:photos/utils/delete_file_util.dart";
|
||||
import "package:photos/utils/dialog_util.dart";
|
||||
import "package:photos/utils/toast_util.dart";
|
||||
|
||||
Future<void> showSingleFileDeleteSheet(
|
||||
BuildContext context,
|
||||
File file, {
|
||||
Function(File)? onFileRemoved,
|
||||
}) async {
|
||||
final List<ButtonWidget> buttons = [];
|
||||
final String fileType = file.fileType == FileType.video ? "video" : "photo";
|
||||
final bool isBothLocalAndRemote =
|
||||
file.uploadedFileID != null && file.localID != null;
|
||||
final bool isLocalOnly = file.uploadedFileID == null && file.localID != null;
|
||||
final bool isRemoteOnly = file.uploadedFileID != null && file.localID == null;
|
||||
const String bodyHighlight = "It will be deleted from all albums.";
|
||||
String body = "";
|
||||
if (isBothLocalAndRemote) {
|
||||
body = "This $fileType is in both ente and your device.";
|
||||
} else if (isRemoteOnly) {
|
||||
body = "This $fileType will be deleted from ente.";
|
||||
} else if (isLocalOnly) {
|
||||
body = "This $fileType will be deleted from your device.";
|
||||
} else {
|
||||
throw AssertionError("Unexpected state");
|
||||
}
|
||||
// Add option to delete from ente
|
||||
if (isBothLocalAndRemote || isRemoteOnly) {
|
||||
buttons.add(
|
||||
ButtonWidget(
|
||||
labelText: isBothLocalAndRemote ? "Delete from ente" : "Yes, delete",
|
||||
buttonType: ButtonType.neutral,
|
||||
buttonSize: ButtonSize.large,
|
||||
shouldStickToDarkTheme: true,
|
||||
buttonAction: ButtonAction.first,
|
||||
shouldSurfaceExecutionStates: true,
|
||||
isInAlert: true,
|
||||
onTap: () async {
|
||||
await deleteFilesFromRemoteOnly(context, [file]);
|
||||
showShortToast(context, "Moved to trash");
|
||||
if (isRemoteOnly) {
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
if (onFileRemoved != null) {
|
||||
onFileRemoved(file);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
// Add option to delete from local
|
||||
if (isBothLocalAndRemote || isLocalOnly) {
|
||||
buttons.add(
|
||||
ButtonWidget(
|
||||
labelText: isBothLocalAndRemote ? "Delete from device" : "Yes, delete",
|
||||
buttonType: ButtonType.neutral,
|
||||
buttonSize: ButtonSize.large,
|
||||
shouldStickToDarkTheme: true,
|
||||
buttonAction: ButtonAction.second,
|
||||
shouldSurfaceExecutionStates: false,
|
||||
isInAlert: true,
|
||||
onTap: () async {
|
||||
await deleteFilesOnDeviceOnly(context, [file]);
|
||||
if (isLocalOnly) {
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
if (onFileRemoved != null) {
|
||||
onFileRemoved(file);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
if (isBothLocalAndRemote) {
|
||||
buttons.add(
|
||||
ButtonWidget(
|
||||
labelText: "Delete from both",
|
||||
buttonType: ButtonType.neutral,
|
||||
buttonSize: ButtonSize.large,
|
||||
shouldStickToDarkTheme: true,
|
||||
buttonAction: ButtonAction.third,
|
||||
shouldSurfaceExecutionStates: true,
|
||||
isInAlert: true,
|
||||
onTap: () async {
|
||||
await deleteFilesFromEverywhere(context, [file]);
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
if (onFileRemoved != null) {
|
||||
onFileRemoved(file);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
buttons.add(
|
||||
const ButtonWidget(
|
||||
labelText: "Cancel",
|
||||
buttonType: ButtonType.secondary,
|
||||
buttonSize: ButtonSize.large,
|
||||
shouldStickToDarkTheme: true,
|
||||
buttonAction: ButtonAction.fourth,
|
||||
isInAlert: true,
|
||||
),
|
||||
);
|
||||
final actionResult = await showActionSheet(
|
||||
context: context,
|
||||
buttons: buttons,
|
||||
actionSheetType: ActionSheetType.defaultActionSheet,
|
||||
body: body,
|
||||
bodyHighlight: bodyHighlight,
|
||||
);
|
||||
if (actionResult?.action != null &&
|
||||
actionResult!.action == ButtonAction.error) {
|
||||
showGenericErrorDialog(context: context);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> showInfoSheet(BuildContext context, File file) async {
|
||||
final colorScheme = getEnteColorScheme(context);
|
||||
return showBarModalBottomSheet(
|
||||
topControl: const SizedBox.shrink(),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0)),
|
||||
backgroundColor: colorScheme.backgroundElevated,
|
||||
barrierColor: backdropFaintDark,
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return Padding(
|
||||
padding:
|
||||
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||
child: FileInfoWidget(file),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
|
@ -597,8 +597,11 @@ class _CollectionActionSheetState extends State<CollectionActionSheet> {
|
|||
}
|
||||
|
||||
Future<bool> _restoreFilesToCollection(int toCollectionID) async {
|
||||
final dialog = createProgressDialog(context, "Restoring files...",
|
||||
isDismissible: true);
|
||||
final dialog = createProgressDialog(
|
||||
context,
|
||||
"Restoring files...",
|
||||
isDismissible: true,
|
||||
);
|
||||
await dialog.show();
|
||||
try {
|
||||
await CollectionsService.instance
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/db/files_db.dart';
|
||||
import 'package:photos/models/magic_metadata.dart';
|
||||
import "package:photos/services/collections_service.dart";
|
||||
import 'package:photos/ui/viewer/gallery/archive_page.dart';
|
||||
import 'package:photos/utils/navigation_util.dart';
|
||||
|
||||
|
@ -15,6 +16,8 @@ class ArchivedCollectionsButtonWidget extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Set<int> hiddenCollectionId =
|
||||
CollectionsService.instance.getHiddenCollections();
|
||||
return OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).backgroundColor,
|
||||
|
@ -43,9 +46,10 @@ class ArchivedCollectionsButtonWidget extends StatelessWidget {
|
|||
),
|
||||
const Padding(padding: EdgeInsets.all(6)),
|
||||
FutureBuilder<int>(
|
||||
future: FilesDB.instance.fileCountWithVisibility(
|
||||
future: FilesDB.instance.archivedFilesCount(
|
||||
visibilityArchive,
|
||||
Configuration.instance.getUserID()!,
|
||||
hiddenCollectionId,
|
||||
),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData && snapshot.data! > 0) {
|
||||
|
|
100
lib/ui/components/album_horizontal_list_widget.dart
Normal file
|
@ -0,0 +1,100 @@
|
|||
import "dart:async";
|
||||
|
||||
import "package:flutter/cupertino.dart";
|
||||
import "package:logging/logging.dart";
|
||||
import "package:photos/core/event_bus.dart";
|
||||
import "package:photos/events/collection_updated_event.dart";
|
||||
import "package:photos/models/collection_items.dart";
|
||||
import "package:photos/ui/collections/collection_item_widget.dart";
|
||||
import "package:photos/ui/common/loading_widget.dart";
|
||||
import "package:photos/ui/components/divider_widget.dart";
|
||||
|
||||
class AlbumHorizontalListWidget extends StatefulWidget {
|
||||
final Future<List<CollectionWithThumbnail>> Function() collectionsFuture;
|
||||
|
||||
const AlbumHorizontalListWidget(
|
||||
this.collectionsFuture, {
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<AlbumHorizontalListWidget> createState() =>
|
||||
_AlbumHorizontalListWidgetState();
|
||||
}
|
||||
|
||||
class _AlbumHorizontalListWidgetState extends State<AlbumHorizontalListWidget> {
|
||||
late StreamSubscription<CollectionUpdatedEvent>
|
||||
_collectionUpdatesSubscription;
|
||||
late Logger _logger;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_collectionUpdatesSubscription =
|
||||
Bus.instance.on<CollectionUpdatedEvent>().listen((event) {
|
||||
setState(() {});
|
||||
});
|
||||
_logger = Logger((_AlbumHorizontalListWidgetState).toString());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_collectionUpdatesSubscription.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
debugPrint('$runtimeType widget build');
|
||||
return FutureBuilder<List<CollectionWithThumbnail>>(
|
||||
future: widget.collectionsFuture(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
_logger.severe("failed to fetch albums", snapshot.error);
|
||||
return const Text("Something went wrong");
|
||||
} else if (snapshot.hasData) {
|
||||
if (snapshot.data!.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
final collectionsWithThumbnail =
|
||||
snapshot.data as List<CollectionWithThumbnail>;
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: SizedBox(
|
||||
height: 190,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: collectionsWithThumbnail.length,
|
||||
padding: const EdgeInsets.fromLTRB(6, 6, 6, 6),
|
||||
itemBuilder: (context, index) {
|
||||
final item = collectionsWithThumbnail[index];
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () async {},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: CollectionItem(
|
||||
item,
|
||||
120,
|
||||
shouldRender: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const DividerWidget(dividerType: DividerType.solid),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const EnteLoadingWidget();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/ente_theme_data.dart';
|
||||
import 'package:photos/theme/colors.dart';
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
import 'package:photos/theme/text_style.dart';
|
||||
import 'package:photos/ui/components/icon_button_widget.dart';
|
||||
|
||||
|
@ -8,6 +9,7 @@ import 'package:photos/ui/components/icon_button_widget.dart';
|
|||
enum NotificationType {
|
||||
warning,
|
||||
banner,
|
||||
goldenBanner,
|
||||
}
|
||||
|
||||
class NotificationWidget extends StatelessWidget {
|
||||
|
@ -30,7 +32,9 @@ class NotificationWidget extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color backgroundColor = Colors.white;
|
||||
final colorScheme = getEnteColorScheme(context);
|
||||
LinearGradient? backgroundGradient;
|
||||
Color? backgroundColor;
|
||||
switch (type) {
|
||||
case NotificationType.warning:
|
||||
backgroundColor = warning500;
|
||||
|
@ -38,6 +42,13 @@ class NotificationWidget extends StatelessWidget {
|
|||
case NotificationType.banner:
|
||||
backgroundColor = backgroundElevated2Dark;
|
||||
break;
|
||||
case NotificationType.goldenBanner:
|
||||
backgroundGradient = LinearGradient(
|
||||
colors: [colorScheme.golden700, colorScheme.golden500],
|
||||
stops: const [0.25, 1],
|
||||
begin: Alignment.bottomCenter,
|
||||
end: Alignment.topCenter,
|
||||
);
|
||||
}
|
||||
return Center(
|
||||
child: GestureDetector(
|
||||
|
@ -49,6 +60,7 @@ class NotificationWidget extends StatelessWidget {
|
|||
),
|
||||
boxShadow: Theme.of(context).colorScheme.enteTheme.shadowMenu,
|
||||
color: backgroundColor,
|
||||
gradient: backgroundGradient,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:logging/logging.dart";
|
||||
import "package:photos/extensions/input_formatter.dart";
|
||||
import "package:photos/models/api/storage_bonus/storage_bonus.dart";
|
||||
import "package:photos/models/user_details.dart";
|
||||
import "package:photos/services/storage_bonus_service.dart";
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
import "package:photos/ui/components/button_widget.dart";
|
||||
|
@ -8,10 +10,18 @@ import "package:photos/ui/components/icon_button_widget.dart";
|
|||
import "package:photos/ui/components/models/button_type.dart";
|
||||
import "package:photos/ui/components/title_bar_title_widget.dart";
|
||||
import "package:photos/ui/components/title_bar_widget.dart";
|
||||
import "package:photos/ui/growth/code_success_screen.dart";
|
||||
import "package:photos/utils/dialog_util.dart";
|
||||
|
||||
class ApplyCodeScreen extends StatefulWidget {
|
||||
const ApplyCodeScreen({super.key});
|
||||
// referrerView and userDetails used to render code_success_screen
|
||||
final ReferralView referralView;
|
||||
final UserDetails userDetails;
|
||||
const ApplyCodeScreen(
|
||||
this.referralView,
|
||||
this.userDetails, {
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ApplyCodeScreen> createState() => _ApplyCodeScreenState();
|
||||
|
@ -112,14 +122,22 @@ class _ApplyCodeScreenState extends State<ApplyCodeScreen> {
|
|||
await StorageBonusService.instance
|
||||
.getGateway()
|
||||
.claimReferralCode(code.trim().toUpperCase());
|
||||
Navigator.of(context).pop();
|
||||
|
||||
Navigator.of(context).pushReplacement(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => CodeSuccessScreen(
|
||||
widget.referralView,
|
||||
widget.userDetails,
|
||||
),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
Logger('$runtimeType')
|
||||
.severe("failed to apply referral", e);
|
||||
showErrorDialogForException(
|
||||
context: context,
|
||||
exception: e as Exception,
|
||||
);
|
||||
context: context,
|
||||
exception: e as Exception,
|
||||
apiErrorPrefix: "Failed to apply code");
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
163
lib/ui/growth/code_success_screen.dart
Normal file
|
@ -0,0 +1,163 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_animate/flutter_animate.dart";
|
||||
import "package:photos/models/api/storage_bonus/storage_bonus.dart";
|
||||
import "package:photos/models/user_details.dart";
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
import "package:photos/ui/components/captioned_text_widget.dart";
|
||||
import "package:photos/ui/components/icon_button_widget.dart";
|
||||
import "package:photos/ui/components/menu_item_widget/menu_item_widget.dart";
|
||||
import "package:photos/ui/components/title_bar_title_widget.dart";
|
||||
import "package:photos/ui/components/title_bar_widget.dart";
|
||||
import "package:photos/ui/growth/referral_code_widget.dart";
|
||||
import "package:photos/ui/growth/storage_details_screen.dart";
|
||||
import "package:photos/utils/navigation_util.dart";
|
||||
import "package:photos/utils/share_util.dart";
|
||||
|
||||
class CodeSuccessScreen extends StatelessWidget {
|
||||
final ReferralView referralView;
|
||||
final UserDetails userDetails;
|
||||
|
||||
const CodeSuccessScreen(this.referralView, this.userDetails, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = getEnteColorScheme(context);
|
||||
final textStyle = getEnteTextTheme(context);
|
||||
return Scaffold(
|
||||
body: CustomScrollView(
|
||||
primary: false,
|
||||
slivers: <Widget>[
|
||||
TitleBarWidget(
|
||||
flexibleSpaceTitle: const TitleBarTitleWidget(
|
||||
title: "Code applied",
|
||||
),
|
||||
actionIcons: [
|
||||
IconButtonWidget(
|
||||
icon: Icons.close_outlined,
|
||||
iconButtonType: IconButtonType.secondary,
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(delegateBuildContext, index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 6,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.check,
|
||||
color: colorScheme.primary500,
|
||||
size: 96,
|
||||
)
|
||||
.animate()
|
||||
.scaleXY(
|
||||
begin: 0.5,
|
||||
end: 1,
|
||||
duration: 750.ms,
|
||||
curve: Curves.easeInOutCubic,
|
||||
delay: 250.ms,
|
||||
)
|
||||
.fadeIn(
|
||||
duration: 500.ms,
|
||||
curve: Curves.easeInOutCubic,
|
||||
),
|
||||
Text(
|
||||
"${referralView.planInfo.storageInGB} GB",
|
||||
style: textStyle.h2Bold,
|
||||
),
|
||||
Text(
|
||||
"Claimed",
|
||||
style: textStyle.body
|
||||
.copyWith(color: colorScheme.textMuted),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: const CaptionedTextWidget(
|
||||
title: "Details",
|
||||
),
|
||||
menuItemColor: colorScheme.fillFaint,
|
||||
trailingWidget: Icon(
|
||||
Icons.chevron_right_outlined,
|
||||
color: colorScheme.strokeBase,
|
||||
),
|
||||
singleBorderRadius: 8,
|
||||
alignCaptionedTextToLeft: true,
|
||||
onTap: () async {
|
||||
routeToPage(
|
||||
context,
|
||||
StorageDetailsScreen(referralView, userDetails),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
shareText(
|
||||
"ente referral code: ${referralView.code} \n\nApply it in Settings → General → Referrals to get ${referralView.planInfo.storageInGB} GB free after you signup for a paid plan\n\nhttps://ente.io",
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: colorScheme.strokeFaint,
|
||||
width: 1,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 12,
|
||||
horizontal: 12,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"Claim more!",
|
||||
style: textStyle.body,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
"${referralView.planInfo.storageInGB} GB each time someone signs up for a paid plan and applies your code",
|
||||
style: textStyle.small
|
||||
.copyWith(color: colorScheme.textMuted),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ReferralCodeWidget(referralView.code),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
"They also get ${referralView.planInfo.storageInGB} GB",
|
||||
style: textStyle.small
|
||||
.copyWith(color: colorScheme.textMuted),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
childCount: 1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
52
lib/ui/growth/referral_code_widget.dart
Normal file
|
@ -0,0 +1,52 @@
|
|||
import "package:dotted_border/dotted_border.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
|
||||
// Figma: https://www.figma.com/file/SYtMyLBs5SAOkTbfMMzhqt/ente-Visual-Design?node-id=11219%3A62974&t=BRCLJhxXP11Q3Wyw-0
|
||||
class ReferralCodeWidget extends StatelessWidget {
|
||||
final String codeValue;
|
||||
|
||||
const ReferralCodeWidget(this.codeValue, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = getEnteColorScheme(context);
|
||||
final textStyle = getEnteTextTheme(context);
|
||||
return Center(
|
||||
child: Container(
|
||||
color: colorScheme.backgroundElevated2,
|
||||
child: DottedBorder(
|
||||
color: colorScheme.strokeMuted,
|
||||
strokeWidth: 1,
|
||||
dashPattern: const [6, 6],
|
||||
radius: const Radius.circular(8),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 26.0,
|
||||
top: 14,
|
||||
right: 12,
|
||||
bottom: 14,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
codeValue,
|
||||
style: textStyle.bodyBold.copyWith(
|
||||
color: colorScheme.primary700,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Icon(
|
||||
Icons.adaptive.share,
|
||||
size: 22,
|
||||
color: colorScheme.strokeMuted,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
import "package:dotted_border/dotted_border.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:photos/models/api/storage_bonus/storage_bonus.dart";
|
||||
import "package:photos/models/user_details.dart";
|
||||
|
@ -14,6 +13,7 @@ import "package:photos/ui/components/menu_item_widget/menu_item_widget.dart";
|
|||
import "package:photos/ui/components/title_bar_title_widget.dart";
|
||||
import "package:photos/ui/components/title_bar_widget.dart";
|
||||
import "package:photos/ui/growth/apply_code_screen.dart";
|
||||
import "package:photos/ui/growth/referral_code_widget.dart";
|
||||
import "package:photos/ui/growth/storage_details_screen.dart";
|
||||
import "package:photos/utils/data_util.dart";
|
||||
import "package:photos/utils/navigation_util.dart";
|
||||
|
@ -129,7 +129,8 @@ class ReferralWidget extends StatelessWidget {
|
|||
? InkWell(
|
||||
onTap: () {
|
||||
shareText(
|
||||
"ente referral code: ${referralView.code} \n\nApply it in Settings → General → Referrals to get 10 GB free after you signup for a paid plan\n\nhttps://ente.io");
|
||||
"ente referral code: ${referralView.code} \n\nApply it in Settings → General → Referrals to get 10 GB free after you signup for a paid plan\n\nhttps://ente.io",
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
|
@ -153,41 +154,7 @@ class ReferralWidget extends StatelessWidget {
|
|||
"friends",
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Center(
|
||||
child: DottedBorder(
|
||||
color: colorScheme.strokeMuted,
|
||||
//color of dotted/dash line
|
||||
strokeWidth: 1,
|
||||
//thickness of dash/dots
|
||||
dashPattern: const [6, 6],
|
||||
radius: const Radius.circular(8),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 26.0,
|
||||
top: 14,
|
||||
right: 12,
|
||||
bottom: 14,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
referralView.code,
|
||||
style: textStyle.bodyBold.copyWith(
|
||||
color: colorScheme.primary700,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Icon(
|
||||
Icons.adaptive.share,
|
||||
size: 22,
|
||||
color: colorScheme.strokeMuted,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
ReferralCodeWidget(referralView.code),
|
||||
const SizedBox(height: 12),
|
||||
const Text(
|
||||
"2. They sign up for a paid plan",
|
||||
|
@ -213,9 +180,11 @@ class ReferralWidget extends StatelessWidget {
|
|||
color: colorScheme.strokeMuted,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text("Referrals are currently paused",
|
||||
style: textStyle.small
|
||||
.copyWith(color: colorScheme.textFaint)),
|
||||
Text(
|
||||
"Referrals are currently paused",
|
||||
style: textStyle.small
|
||||
.copyWith(color: colorScheme.textFaint),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -247,7 +216,7 @@ class ReferralWidget extends StatelessWidget {
|
|||
onTap: () async {
|
||||
await routeToPage(
|
||||
context,
|
||||
const ApplyCodeScreen(),
|
||||
ApplyCodeScreen(referralView, userDetails),
|
||||
);
|
||||
notifyParent();
|
||||
},
|
||||
|
@ -273,9 +242,12 @@ class ReferralWidget extends StatelessWidget {
|
|||
alignCaptionedTextToLeft: true,
|
||||
onTap: () async {
|
||||
routeToPage(
|
||||
context,
|
||||
const WebPage(
|
||||
"FAQ", "https://ente.io/faq/general/referral-program"));
|
||||
context,
|
||||
const WebPage(
|
||||
"FAQ",
|
||||
"https://ente.io/faq/general/referral-program",
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
|
|
@ -108,19 +108,23 @@ class _StorageDetailsScreenState extends State<StorageDetailsScreen> {
|
|||
BonusInfoSection(
|
||||
sectionName: "Free storage claimed",
|
||||
leftValue: convertBytesToAbsoluteGBs(
|
||||
widget.referralView.claimedStorage),
|
||||
widget.referralView.claimedStorage,
|
||||
),
|
||||
leftUnitName: "GB",
|
||||
rightValue: null,
|
||||
),
|
||||
BonusInfoSection(
|
||||
sectionName: "Free storage usable",
|
||||
leftValue: convertBytesToAbsoluteGBs(min(
|
||||
widget.referralView.claimedStorage,
|
||||
widget.userDetails.getTotalStorage(),
|
||||
)),
|
||||
leftValue: convertBytesToAbsoluteGBs(
|
||||
min(
|
||||
widget.referralView.claimedStorage,
|
||||
widget.userDetails.getTotalStorage(),
|
||||
),
|
||||
),
|
||||
leftUnitName: "GB",
|
||||
rightValue: convertBytesToAbsoluteGBs(
|
||||
widget.userDetails.getTotalStorage()),
|
||||
widget.userDetails.getTotalStorage(),
|
||||
),
|
||||
rightUnitName: "GB",
|
||||
),
|
||||
const SizedBox(
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import "dart:io";
|
||||
|
||||
import "package:flutter/cupertino.dart";
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/ente_theme_data.dart';
|
||||
import 'package:photos/models/memory.dart';
|
||||
import 'package:photos/services/memories_service.dart';
|
||||
import 'package:photos/ui/extents_page_view.dart';
|
||||
import "package:photos/ui/actions/file/file_actions.dart";
|
||||
import 'package:photos/ui/viewer/file/file_widget.dart';
|
||||
import 'package:photos/ui/viewer/file/thumbnail_widget.dart';
|
||||
import 'package:photos/utils/date_time_util.dart';
|
||||
|
@ -315,6 +318,22 @@ class _FullScreenMemoryState extends State<FullScreenMemory> {
|
|||
);
|
||||
}
|
||||
|
||||
void onFileDeleted() {
|
||||
if (widget.memories.length == 1) {
|
||||
Navigator.pop(context);
|
||||
} else {
|
||||
setState(() {
|
||||
if (_index != 0) {
|
||||
_pageController?.jumpToPage(_index - 1);
|
||||
}
|
||||
widget.memories.removeAt(_index);
|
||||
if (_index != 0) {
|
||||
_index--;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Hero _buildInfoText() {
|
||||
return Hero(
|
||||
tag: widget.title,
|
||||
|
@ -346,17 +365,48 @@ class _FullScreenMemoryState extends State<FullScreenMemory> {
|
|||
|
||||
Widget _buildBottomIcons() {
|
||||
final file = widget.memories[_index].file;
|
||||
return Container(
|
||||
alignment: Alignment.bottomRight,
|
||||
padding: const EdgeInsets.fromLTRB(0, 0, 26, 20),
|
||||
child: IconButton(
|
||||
icon: Icon(
|
||||
Icons.adaptive.share,
|
||||
color: Colors.white, //same for both themes
|
||||
return SafeArea(
|
||||
child: Container(
|
||||
alignment: Alignment.bottomRight,
|
||||
padding: const EdgeInsets.fromLTRB(26, 0, 26, 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Platform.isAndroid ? Icons.info_outline : CupertinoIcons.info,
|
||||
color: Colors.white, //same for both themes
|
||||
),
|
||||
onPressed: () {
|
||||
showInfoSheet(context, file);
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Platform.isAndroid
|
||||
? Icons.delete_outline
|
||||
: CupertinoIcons.delete,
|
||||
color: Colors.white, //same for both themes
|
||||
),
|
||||
onPressed: () async {
|
||||
await showSingleFileDeleteSheet(
|
||||
context,
|
||||
file,
|
||||
onFileRemoved: (file) => {onFileDeleted()},
|
||||
);
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.adaptive.share,
|
||||
color: Colors.white, //same for both themes
|
||||
),
|
||||
onPressed: () {
|
||||
share(context, [file]);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
onPressed: () {
|
||||
share(context, [file]);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -381,7 +431,7 @@ class _FullScreenMemoryState extends State<FullScreenMemory> {
|
|||
|
||||
Widget _buildSwiper() {
|
||||
_pageController = PageController(initialPage: _index);
|
||||
return ExtentsPageView.extents(
|
||||
return PageView.builder(
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
if (index < widget.memories.length - 1) {
|
||||
final nextFile = widget.memories[index + 1].file;
|
||||
|
@ -405,7 +455,6 @@ class _FullScreenMemoryState extends State<FullScreenMemory> {
|
|||
},
|
||||
itemCount: widget.memories.length,
|
||||
controller: _pageController,
|
||||
extents: 1,
|
||||
onPageChanged: (index) async {
|
||||
await MemoriesService.instance.markMemoryAsSeen(widget.memories[index]);
|
||||
if (mounted) {
|
||||
|
|
|
@ -265,7 +265,7 @@ class _HomeWidgetState extends State<HomeWidget> {
|
|||
_logger.info("Building home_Widget with tab $_selectedTabIndex");
|
||||
bool isSettingsOpen = false;
|
||||
final enableDrawer = LocalSyncService.instance.hasCompletedFirstImport();
|
||||
|
||||
final action = AppLifecycleService.instance.mediaExtensionAction.action;
|
||||
return UserDetailsStateWidget(
|
||||
child: WillPopScope(
|
||||
child: Scaffold(
|
||||
|
@ -298,9 +298,7 @@ class _HomeWidgetState extends State<HomeWidget> {
|
|||
Navigator.pop(context);
|
||||
return false;
|
||||
}
|
||||
if (Platform.isAndroid &&
|
||||
AppLifecycleService.instance.intentAction ==
|
||||
IntentAction.main) {
|
||||
if (Platform.isAndroid && action == IntentAction.main) {
|
||||
MoveToBackground.moveTaskToBack();
|
||||
return false;
|
||||
} else {
|
||||
|
|
|
@ -311,7 +311,6 @@ class _LazyLoadingGridViewState extends State<LazyLoadingGridView> {
|
|||
bool? _shouldRender;
|
||||
int? _currentUserID;
|
||||
late StreamSubscription<ClearSelectionsEvent> _clearSelectionsEvent;
|
||||
final _mediaExtensionPlugin = MediaExtension();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -426,16 +425,18 @@ class _LazyLoadingGridViewState extends State<LazyLoadingGridView> {
|
|||
if (widget.selectedFiles.files.isNotEmpty) {
|
||||
_selectFile(file);
|
||||
} else {
|
||||
if (AppLifecycleService.instance.intentAction == IntentAction.pick) {
|
||||
if (AppLifecycleService.instance.mediaExtensionAction.action ==
|
||||
IntentAction.pick) {
|
||||
final ioFile = await getFile(file);
|
||||
_mediaExtensionPlugin.setResult("file://${ioFile!.path}");
|
||||
MediaExtension().setResult("file://${ioFile!.path}");
|
||||
} else {
|
||||
_routeToDetailPage(file, context);
|
||||
}
|
||||
}
|
||||
},
|
||||
onLongPress: () {
|
||||
if (AppLifecycleService.instance.intentAction == IntentAction.main) {
|
||||
if (AppLifecycleService.instance.mediaExtensionAction.action ==
|
||||
IntentAction.main) {
|
||||
HapticFeedback.lightImpact();
|
||||
_selectFile(file);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:io';
|
|||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import "package:flutter_animate/flutter_animate.dart";
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
import 'package:photos/events/opened_settings_event.dart';
|
||||
|
@ -77,19 +78,25 @@ class SettingsPage extends StatelessWidget {
|
|||
contents.addAll([
|
||||
const StorageCardWidget(),
|
||||
StorageBonusService.instance.shouldShowStorageBonus()
|
||||
? Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: NotificationWidget(
|
||||
startIcon: Icons.auto_awesome,
|
||||
actionIcon: Icons.arrow_forward_outlined,
|
||||
text: "Double your storage",
|
||||
subText: "Refer friends and 2x your plan",
|
||||
type: NotificationType.banner,
|
||||
onTap: () async {
|
||||
StorageBonusService.instance.markStorageBonusAsDone();
|
||||
routeToPage(context, const ReferralScreen());
|
||||
},
|
||||
),
|
||||
? RepaintBoundary(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: NotificationWidget(
|
||||
startIcon: Icons.auto_awesome,
|
||||
actionIcon: Icons.arrow_forward_outlined,
|
||||
text: "Double your storage",
|
||||
subText: "Refer friends and 2x your plan",
|
||||
type: NotificationType.goldenBanner,
|
||||
onTap: () async {
|
||||
StorageBonusService.instance.markStorageBonusAsDone();
|
||||
routeToPage(context, const ReferralScreen());
|
||||
},
|
||||
),
|
||||
).animate(onPlay: (controller) => controller.repeat()).shimmer(
|
||||
duration: 1000.ms,
|
||||
delay: 3200.ms,
|
||||
size: 0.6,
|
||||
),
|
||||
)
|
||||
: const SizedBox(height: 12),
|
||||
const BackupSectionWidget(),
|
||||
|
|
|
@ -228,7 +228,6 @@ class _ShareCollectionPageState extends State<ShareCollectionPage> {
|
|||
),
|
||||
leadingIcon: Icons.link,
|
||||
menuItemColor: getEnteColorScheme(context).fillFaint,
|
||||
isBottomBorderRadiusRemoved: true,
|
||||
showOnlyLoadingState: true,
|
||||
onTap: () async {
|
||||
final bool result =
|
||||
|
|
|
@ -100,8 +100,9 @@ class _DeleteEmptyAlbumsState extends State<DeleteEmptyAlbums> {
|
|||
"${collections.length}";
|
||||
try {
|
||||
await CollectionsService.instance.trashEmptyCollection(
|
||||
collections[i].collection,
|
||||
isBulkDelete: true);
|
||||
collections[i].collection,
|
||||
isBulkDelete: true,
|
||||
);
|
||||
} catch (_) {
|
||||
failedCount++;
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ class _FileSelectionActionWidgetState extends State<FileSelectionActionWidget> {
|
|||
late CollectionActions collectionActions;
|
||||
late bool isCollectionOwner;
|
||||
|
||||
// _cachedCollectionForSharedLink is primarly used to avoid creating duplicate
|
||||
// _cachedCollectionForSharedLink is primarily used to avoid creating duplicate
|
||||
// links if user keeps on creating Create link button after selecting
|
||||
// few files. This link is reset on any selection changed;
|
||||
Collection? _cachedCollectionForSharedLink;
|
||||
|
|
104
lib/ui/viewer/actions/file_viewer.dart
Normal file
|
@ -0,0 +1,104 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import "package:chewie/chewie.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter/services.dart";
|
||||
import "package:logging/logging.dart";
|
||||
import "package:media_extension/media_extension_action_types.dart";
|
||||
import "package:photo_view/photo_view.dart";
|
||||
import "package:photos/services/app_lifecycle_service.dart";
|
||||
|
||||
import "package:video_player/video_player.dart";
|
||||
|
||||
class FileViewer extends StatefulWidget {
|
||||
const FileViewer({super.key});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return FileViewerState();
|
||||
}
|
||||
}
|
||||
|
||||
class FileViewerState extends State<FileViewer> {
|
||||
final action = AppLifecycleService.instance.mediaExtensionAction;
|
||||
ChewieController? controller;
|
||||
VideoPlayerController? videoController;
|
||||
final Logger _logger = Logger("FileViewer");
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (action.type == MediaType.video) {
|
||||
initController();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
videoController?.dispose();
|
||||
controller?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void initController() async {
|
||||
videoController = VideoPlayerController.contentUri(
|
||||
Uri.parse(action.data!),
|
||||
);
|
||||
controller = ChewieController(
|
||||
videoPlayerController: videoController!,
|
||||
autoInitialize: true,
|
||||
aspectRatio: 16 / 9,
|
||||
autoPlay: true,
|
||||
looping: true,
|
||||
showOptions: false,
|
||||
materialProgressColors: ChewieProgressColors(
|
||||
playedColor: const Color.fromRGBO(45, 194, 98, 1.0),
|
||||
handleColor: Colors.white,
|
||||
bufferedColor: Colors.white,
|
||||
),
|
||||
);
|
||||
controller!.addListener(() {
|
||||
if (!controller!.isFullScreen) {
|
||||
SystemChrome.setPreferredOrientations(
|
||||
[DeviceOrientation.portraitUp],
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
onPressed: () {
|
||||
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
|
||||
},
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
),
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: (() {
|
||||
if (action.type == MediaType.image) {
|
||||
return PhotoView(
|
||||
imageProvider: MemoryImage(base64Decode(action.data!)),
|
||||
);
|
||||
} else if (action.type == MediaType.video) {
|
||||
return controller != null
|
||||
? Chewie(controller: controller!)
|
||||
: const CircularProgressIndicator();
|
||||
} else {
|
||||
_logger.severe('unsupported file type ${action.type}');
|
||||
return const Icon(Icons.error);
|
||||
}
|
||||
})(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -124,6 +124,8 @@ class _DetailPageState extends State<DetailPage> {
|
|||
_files![_selectedIndex],
|
||||
_onEditFileRequested,
|
||||
widget.config.mode == DetailPageMode.minimalistic,
|
||||
onFileRemoved: _onFileRemoved,
|
||||
userID: Configuration.instance.getUserID(),
|
||||
key: _bottomBarKey,
|
||||
),
|
||||
],
|
||||
|
|
|
@ -14,6 +14,7 @@ import 'package:photos/events/local_photos_updated_event.dart';
|
|||
import 'package:photos/models/file.dart';
|
||||
import 'package:photos/models/file_type.dart';
|
||||
import 'package:photos/models/ignored_file.dart';
|
||||
import "package:photos/models/magic_metadata.dart";
|
||||
import 'package:photos/models/selected_files.dart';
|
||||
import 'package:photos/models/trash_file.dart';
|
||||
import 'package:photos/services/collections_service.dart';
|
||||
|
@ -23,13 +24,10 @@ import 'package:photos/services/ignored_files_service.dart';
|
|||
import 'package:photos/services/local_sync_service.dart';
|
||||
import 'package:photos/ui/collection_action_sheet.dart';
|
||||
import 'package:photos/ui/common/progress_dialog.dart';
|
||||
import 'package:photos/ui/components/action_sheet_widget.dart';
|
||||
import 'package:photos/ui/components/button_widget.dart';
|
||||
import 'package:photos/ui/components/models/button_type.dart';
|
||||
import 'package:photos/ui/viewer/file/custom_app_bar.dart';
|
||||
import 'package:photos/utils/delete_file_util.dart';
|
||||
import 'package:photos/utils/dialog_util.dart';
|
||||
import 'package:photos/utils/file_util.dart';
|
||||
import "package:photos/utils/magic_util.dart";
|
||||
import 'package:photos/utils/toast_util.dart';
|
||||
|
||||
class FadingAppBar extends StatefulWidget implements PreferredSizeWidget {
|
||||
|
@ -148,22 +146,22 @@ class FadingAppBarState extends State<FadingAppBar> {
|
|||
);
|
||||
}
|
||||
// options for files owned by the user
|
||||
if (isOwnedByUser) {
|
||||
if (isOwnedByUser && !isFileHidden) {
|
||||
final bool isArchived =
|
||||
widget.file.magicMetadata.visibility == visibilityArchive;
|
||||
items.add(
|
||||
PopupMenuItem(
|
||||
value: 2,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Platform.isAndroid
|
||||
? Icons.delete_outline
|
||||
: CupertinoIcons.delete,
|
||||
isArchived ? Icons.unarchive : Icons.archive_outlined,
|
||||
color: Theme.of(context).iconTheme.color,
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
),
|
||||
const Text("Delete"),
|
||||
Text(isArchived ? "Unarchive" : "Archive"),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -235,7 +233,7 @@ class FadingAppBarState extends State<FadingAppBar> {
|
|||
if (value == 1) {
|
||||
_download(widget.file);
|
||||
} else if (value == 2) {
|
||||
await _showSingleFileDeleteSheet(widget.file);
|
||||
await _toggleFileArchiveStatus(widget.file);
|
||||
} else if (value == 3) {
|
||||
_setAs(widget.file);
|
||||
} else if (value == 4) {
|
||||
|
@ -337,109 +335,16 @@ class FadingAppBarState extends State<FadingAppBar> {
|
|||
);
|
||||
}
|
||||
|
||||
Future<void> _showSingleFileDeleteSheet(File file) async {
|
||||
final List<ButtonWidget> buttons = [];
|
||||
final String fileType = file.fileType == FileType.video ? "video" : "photo";
|
||||
final bool isBothLocalAndRemote =
|
||||
file.uploadedFileID != null && file.localID != null;
|
||||
final bool isLocalOnly =
|
||||
file.uploadedFileID == null && file.localID != null;
|
||||
final bool isRemoteOnly =
|
||||
file.uploadedFileID != null && file.localID == null;
|
||||
const String bodyHighlight = "It will be deleted from all albums.";
|
||||
String body = "";
|
||||
if (isBothLocalAndRemote) {
|
||||
body = "This $fileType is in both ente and your device.";
|
||||
} else if (isRemoteOnly) {
|
||||
body = "This $fileType will be deleted from ente.";
|
||||
} else if (isLocalOnly) {
|
||||
body = "This $fileType will be deleted from your device.";
|
||||
} else {
|
||||
throw AssertionError("Unexpected state");
|
||||
}
|
||||
// Add option to delete from ente
|
||||
if (isBothLocalAndRemote || isRemoteOnly) {
|
||||
buttons.add(
|
||||
ButtonWidget(
|
||||
labelText: isBothLocalAndRemote ? "Delete from ente" : "Yes, delete",
|
||||
buttonType: ButtonType.neutral,
|
||||
buttonSize: ButtonSize.large,
|
||||
shouldStickToDarkTheme: true,
|
||||
buttonAction: ButtonAction.first,
|
||||
shouldSurfaceExecutionStates: true,
|
||||
isInAlert: true,
|
||||
onTap: () async {
|
||||
await deleteFilesFromRemoteOnly(context, [file]);
|
||||
showShortToast(context, "Moved to trash");
|
||||
if (isRemoteOnly) {
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
widget.onFileRemoved(file);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
// Add option to delete from local
|
||||
if (isBothLocalAndRemote || isLocalOnly) {
|
||||
buttons.add(
|
||||
ButtonWidget(
|
||||
labelText:
|
||||
isBothLocalAndRemote ? "Delete from device" : "Yes, delete",
|
||||
buttonType: ButtonType.neutral,
|
||||
buttonSize: ButtonSize.large,
|
||||
shouldStickToDarkTheme: true,
|
||||
buttonAction: ButtonAction.second,
|
||||
shouldSurfaceExecutionStates: false,
|
||||
isInAlert: true,
|
||||
onTap: () async {
|
||||
await deleteFilesOnDeviceOnly(context, [file]);
|
||||
if (isLocalOnly) {
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
widget.onFileRemoved(file);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (isBothLocalAndRemote) {
|
||||
buttons.add(
|
||||
ButtonWidget(
|
||||
labelText: "Delete from both",
|
||||
buttonType: ButtonType.neutral,
|
||||
buttonSize: ButtonSize.large,
|
||||
shouldStickToDarkTheme: true,
|
||||
buttonAction: ButtonAction.third,
|
||||
shouldSurfaceExecutionStates: true,
|
||||
isInAlert: true,
|
||||
onTap: () async {
|
||||
await deleteFilesFromEverywhere(context, [file]);
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
widget.onFileRemoved(file);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
buttons.add(
|
||||
const ButtonWidget(
|
||||
labelText: "Cancel",
|
||||
buttonType: ButtonType.secondary,
|
||||
buttonSize: ButtonSize.large,
|
||||
shouldStickToDarkTheme: true,
|
||||
buttonAction: ButtonAction.fourth,
|
||||
isInAlert: true,
|
||||
),
|
||||
Future<void> _toggleFileArchiveStatus(File file) async {
|
||||
final bool isArchived =
|
||||
widget.file.magicMetadata.visibility == visibilityArchive;
|
||||
await changeVisibility(
|
||||
context,
|
||||
[widget.file],
|
||||
isArchived ? visibilityVisible : visibilityArchive,
|
||||
);
|
||||
final actionResult = await showActionSheet(
|
||||
context: context,
|
||||
buttons: buttons,
|
||||
actionSheetType: ActionSheetType.defaultActionSheet,
|
||||
body: body,
|
||||
bodyHighlight: bodyHighlight,
|
||||
);
|
||||
if (actionResult?.action != null &&
|
||||
actionResult!.action == ButtonAction.error) {
|
||||
showGenericErrorDialog(context: context);
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,31 +2,30 @@ import 'dart:io';
|
|||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/models/file.dart';
|
||||
import 'package:photos/models/file_type.dart';
|
||||
import 'package:photos/models/magic_metadata.dart';
|
||||
import 'package:photos/models/selected_files.dart';
|
||||
import 'package:photos/models/trash_file.dart';
|
||||
import 'package:photos/services/collections_service.dart';
|
||||
import 'package:photos/theme/colors.dart';
|
||||
import 'package:photos/theme/ente_theme.dart';
|
||||
import "package:photos/ui/actions/file/file_actions.dart";
|
||||
import 'package:photos/ui/collection_action_sheet.dart';
|
||||
import 'package:photos/ui/viewer/file/file_info_widget.dart';
|
||||
import 'package:photos/utils/delete_file_util.dart';
|
||||
import 'package:photos/utils/magic_util.dart';
|
||||
import 'package:photos/utils/share_util.dart';
|
||||
|
||||
class FadingBottomBar extends StatefulWidget {
|
||||
final File file;
|
||||
final Function(File) onEditRequested;
|
||||
final Function(File) onFileRemoved;
|
||||
final bool showOnlyInfoButton;
|
||||
final int? userID;
|
||||
|
||||
const FadingBottomBar(
|
||||
this.file,
|
||||
this.onEditRequested,
|
||||
this.showOnlyInfoButton, {
|
||||
required this.onFileRemoved,
|
||||
this.userID,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
|
@ -63,6 +62,8 @@ class FadingBottomBarState extends State<FadingBottomBar> {
|
|||
|
||||
Widget _getBottomBar() {
|
||||
final List<Widget> children = [];
|
||||
final bool isOwnedByUser =
|
||||
widget.file.ownerID == null || widget.file.ownerID == widget.userID;
|
||||
children.add(
|
||||
Tooltip(
|
||||
message: "Info",
|
||||
|
@ -88,15 +89,7 @@ class FadingBottomBarState extends State<FadingBottomBar> {
|
|||
if (widget.file is TrashFile) {
|
||||
_addTrashOptions(children);
|
||||
}
|
||||
final bool isUploadedByUser = widget.file.uploadedFileID != null &&
|
||||
widget.file.ownerID == Configuration.instance.getUserID();
|
||||
bool isFileHidden = false;
|
||||
if (isUploadedByUser) {
|
||||
isFileHidden = CollectionsService.instance
|
||||
.getCollectionByID(widget.file.collectionID!)
|
||||
?.isHidden() ??
|
||||
false;
|
||||
}
|
||||
|
||||
if (!widget.showOnlyInfoButton && widget.file is! TrashFile) {
|
||||
if (widget.file.fileType == FileType.image ||
|
||||
widget.file.fileType == FileType.livePhoto) {
|
||||
|
@ -118,26 +111,21 @@ class FadingBottomBarState extends State<FadingBottomBar> {
|
|||
),
|
||||
);
|
||||
}
|
||||
if (isUploadedByUser && !isFileHidden) {
|
||||
final bool isArchived =
|
||||
widget.file.magicMetadata.visibility == visibilityArchive;
|
||||
if (isOwnedByUser) {
|
||||
children.add(
|
||||
Tooltip(
|
||||
message: isArchived ? "Unarchive" : "Archive",
|
||||
message: "Delete",
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 12, bottom: 12),
|
||||
child: IconButton(
|
||||
icon: Icon(
|
||||
isArchived ? Icons.unarchive : Icons.archive_outlined,
|
||||
Platform.isAndroid
|
||||
? Icons.delete_outline
|
||||
: CupertinoIcons.delete,
|
||||
color: Colors.white,
|
||||
),
|
||||
onPressed: () async {
|
||||
await changeVisibility(
|
||||
context,
|
||||
[widget.file],
|
||||
isArchived ? visibilityVisible : visibilityArchive,
|
||||
);
|
||||
safeRefresh();
|
||||
await _showSingleFileDeleteSheet(widget.file);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
@ -223,6 +211,14 @@ class FadingBottomBarState extends State<FadingBottomBar> {
|
|||
);
|
||||
}
|
||||
|
||||
Future<void> _showSingleFileDeleteSheet(File file) async {
|
||||
await showSingleFileDeleteSheet(
|
||||
context,
|
||||
file,
|
||||
onFileRemoved: widget.onFileRemoved,
|
||||
);
|
||||
}
|
||||
|
||||
void _addTrashOptions(List<Widget> children) {
|
||||
children.add(
|
||||
Tooltip(
|
||||
|
@ -272,20 +268,6 @@ class FadingBottomBarState extends State<FadingBottomBar> {
|
|||
}
|
||||
|
||||
Future<void> _displayInfo(File file) async {
|
||||
final colorScheme = getEnteColorScheme(context);
|
||||
return showBarModalBottomSheet(
|
||||
topControl: const SizedBox.shrink(),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0)),
|
||||
backgroundColor: colorScheme.backgroundElevated,
|
||||
barrierColor: backdropFaintDark,
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return Padding(
|
||||
padding:
|
||||
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||
child: FileInfoWidget(file),
|
||||
);
|
||||
},
|
||||
);
|
||||
await showInfoSheet(context, file);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
import 'package:collection/collection.dart' show IterableExtension;
|
||||
import 'package:flutter/material.dart';
|
||||
import "package:logging/logging.dart";
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
import 'package:photos/db/files_db.dart';
|
||||
import 'package:photos/events/files_updated_event.dart';
|
||||
import "package:photos/models/collection_items.dart";
|
||||
import 'package:photos/models/gallery_type.dart';
|
||||
import 'package:photos/models/magic_metadata.dart';
|
||||
import 'package:photos/models/selected_files.dart';
|
||||
import 'package:photos/services/collections_service.dart';
|
||||
import "package:photos/ui/collections/collection_item_widget.dart";
|
||||
import "package:photos/ui/common/loading_widget.dart";
|
||||
import "package:photos/ui/components/album_horizontal_list_widget.dart";
|
||||
import 'package:photos/ui/viewer/actions/file_selection_overlay_bar.dart';
|
||||
import "package:photos/ui/viewer/gallery/empty_state.dart";
|
||||
import 'package:photos/ui/viewer/gallery/gallery.dart';
|
||||
|
@ -22,7 +19,6 @@ class ArchivePage extends StatelessWidget {
|
|||
final GalleryType appBarType;
|
||||
final GalleryType overlayType;
|
||||
final _selectedFiles = SelectedFiles();
|
||||
final Logger _logger = Logger("ArchivePage");
|
||||
|
||||
ArchivePage({
|
||||
this.tagPrefix = "archived_page",
|
||||
|
@ -70,43 +66,8 @@ class ArchivePage extends StatelessWidget {
|
|||
emptyState: const EmptyState(
|
||||
text: "You don't have any archived items.",
|
||||
),
|
||||
header: FutureBuilder(
|
||||
future: CollectionsService.instance.getArchivedCollectionWithThumb(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
_logger.severe("failed to fetch archived albums", snapshot.error);
|
||||
return const Text("Something went wrong");
|
||||
} else if (snapshot.hasData) {
|
||||
final collectionsWithThumbnail =
|
||||
snapshot.data as List<CollectionWithThumbnail>;
|
||||
return SizedBox(
|
||||
height: 200,
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: collectionsWithThumbnail.length,
|
||||
padding: const EdgeInsets.fromLTRB(6, 6, 6, 6),
|
||||
itemBuilder: (context, index) {
|
||||
final item = collectionsWithThumbnail[index];
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () async {},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: CollectionItem(
|
||||
item,
|
||||
120,
|
||||
shouldRender: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const EnteLoadingWidget();
|
||||
}
|
||||
},
|
||||
header: AlbumHorizontalListWidget(
|
||||
CollectionsService.instance.getArchivedCollectionWithThumb,
|
||||
),
|
||||
);
|
||||
return Scaffold(
|
||||
|
|
|
@ -16,13 +16,13 @@ import 'package:photos/ui/viewer/gallery/empty_state.dart';
|
|||
import 'package:photos/ui/viewer/gallery/gallery.dart';
|
||||
import 'package:photos/ui/viewer/gallery/gallery_app_bar_widget.dart';
|
||||
|
||||
class CollectionPage extends StatefulWidget {
|
||||
class CollectionPage extends StatelessWidget {
|
||||
final CollectionWithThumbnail c;
|
||||
final String tagPrefix;
|
||||
final GalleryType appBarType;
|
||||
final bool hasVerifiedLock;
|
||||
|
||||
const CollectionPage(
|
||||
CollectionPage(
|
||||
this.c, {
|
||||
this.tagPrefix = "collection",
|
||||
this.appBarType = GalleryType.ownedCollection,
|
||||
|
@ -30,42 +30,24 @@ class CollectionPage extends StatefulWidget {
|
|||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<CollectionPage> createState() => _CollectionPageState();
|
||||
}
|
||||
|
||||
class _CollectionPageState extends State<CollectionPage> {
|
||||
final _selectedFiles = SelectedFiles();
|
||||
|
||||
final GlobalKey shareButtonKey = GlobalKey();
|
||||
final ValueNotifier<double> _bottomPosition = ValueNotifier(-150.0);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_selectedFiles.addListener(_selectedFilesListener);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_selectedFiles.removeListener(_selectedFilesListener);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(Object context) {
|
||||
if (widget.hasVerifiedLock == false && widget.c.collection.isHidden()) {
|
||||
if (hasVerifiedLock == false && c.collection.isHidden()) {
|
||||
return const EmptyState();
|
||||
}
|
||||
|
||||
final appBarTypeValue = _getGalleryType(widget.c.collection);
|
||||
final appBarTypeValue = _getGalleryType(c.collection);
|
||||
final List<File>? initialFiles =
|
||||
widget.c.thumbnail != null ? [widget.c.thumbnail!] : null;
|
||||
c.thumbnail != null ? [c.thumbnail!] : null;
|
||||
final gallery = Gallery(
|
||||
asyncLoader: (creationStartTime, creationEndTime, {limit, asc}) async {
|
||||
final FileLoadResult result =
|
||||
await FilesDB.instance.getFilesInCollection(
|
||||
widget.c.collection.id,
|
||||
c.collection.id,
|
||||
creationStartTime,
|
||||
creationEndTime,
|
||||
limit: limit,
|
||||
|
@ -82,25 +64,25 @@ class _CollectionPageState extends State<CollectionPage> {
|
|||
},
|
||||
reloadEvent: Bus.instance
|
||||
.on<CollectionUpdatedEvent>()
|
||||
.where((event) => event.collectionID == widget.c.collection.id),
|
||||
.where((event) => event.collectionID == c.collection.id),
|
||||
removalEventTypes: const {
|
||||
EventType.deletedFromRemote,
|
||||
EventType.deletedFromEverywhere,
|
||||
EventType.hide,
|
||||
},
|
||||
tagPrefix: widget.tagPrefix,
|
||||
tagPrefix: tagPrefix,
|
||||
selectedFiles: _selectedFiles,
|
||||
initialFiles: initialFiles,
|
||||
albumName: widget.c.collection.name,
|
||||
albumName: c.collection.name,
|
||||
);
|
||||
return Scaffold(
|
||||
appBar: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(50.0),
|
||||
child: GalleryAppBarWidget(
|
||||
appBarTypeValue,
|
||||
widget.c.collection.name,
|
||||
c.collection.name,
|
||||
_selectedFiles,
|
||||
collection: widget.c.collection,
|
||||
collection: c.collection,
|
||||
),
|
||||
),
|
||||
body: Stack(
|
||||
|
@ -110,7 +92,7 @@ class _CollectionPageState extends State<CollectionPage> {
|
|||
FileSelectionOverlayBar(
|
||||
appBarTypeValue,
|
||||
_selectedFiles,
|
||||
collection: widget.c.collection,
|
||||
collection: c.collection,
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -129,12 +111,6 @@ class _CollectionPageState extends State<CollectionPage> {
|
|||
} else if (c.type == CollectionType.favorites) {
|
||||
return GalleryType.favorite;
|
||||
}
|
||||
return widget.appBarType;
|
||||
}
|
||||
|
||||
_selectedFilesListener() {
|
||||
_selectedFiles.files.isNotEmpty
|
||||
? _bottomPosition.value = 0.0
|
||||
: _bottomPosition.value = -150.0;
|
||||
return appBarType;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import 'package:photos/ui/viewer/gallery/gallery.dart';
|
|||
import 'package:photos/ui/viewer/gallery/gallery_app_bar_widget.dart';
|
||||
import 'package:photos/utils/delete_file_util.dart';
|
||||
|
||||
class TrashPage extends StatefulWidget {
|
||||
class TrashPage extends StatelessWidget {
|
||||
final String tagPrefix;
|
||||
final GalleryType appBarType;
|
||||
final GalleryType overlayType;
|
||||
|
@ -26,30 +26,9 @@ class TrashPage extends StatefulWidget {
|
|||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<TrashPage> createState() => _TrashPageState();
|
||||
}
|
||||
|
||||
class _TrashPageState extends State<TrashPage> {
|
||||
late Function() _selectedFilesListener;
|
||||
@override
|
||||
void initState() {
|
||||
_selectedFilesListener = () {
|
||||
setState(() {});
|
||||
};
|
||||
widget._selectedFiles.addListener(_selectedFilesListener);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
widget._selectedFiles.removeListener(_selectedFilesListener);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(Object context) {
|
||||
final bool filesAreSelected = widget._selectedFiles.files.isNotEmpty;
|
||||
final bool filesAreSelected = _selectedFiles.files.isNotEmpty;
|
||||
|
||||
final gallery = Gallery(
|
||||
asyncLoader: (creationStartTime, creationEndTime, {limit, asc}) {
|
||||
|
@ -70,8 +49,8 @@ class _TrashPageState extends State<TrashPage> {
|
|||
forceReloadEvents: [
|
||||
Bus.instance.on<ForceReloadTrashPageEvent>(),
|
||||
],
|
||||
tagPrefix: widget.tagPrefix,
|
||||
selectedFiles: widget._selectedFiles,
|
||||
tagPrefix: tagPrefix,
|
||||
selectedFiles: _selectedFiles,
|
||||
header: _headerWidget(),
|
||||
initialFiles: null,
|
||||
);
|
||||
|
@ -80,9 +59,9 @@ class _TrashPageState extends State<TrashPage> {
|
|||
appBar: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(50.0),
|
||||
child: GalleryAppBarWidget(
|
||||
widget.appBarType,
|
||||
appBarType,
|
||||
"Trash",
|
||||
widget._selectedFiles,
|
||||
_selectedFiles,
|
||||
),
|
||||
),
|
||||
body: Stack(
|
||||
|
@ -109,7 +88,7 @@ class _TrashPageState extends State<TrashPage> {
|
|||
),
|
||||
),
|
||||
),
|
||||
FileSelectionOverlayBar(GalleryType.trash, widget._selectedFiles)
|
||||
FileSelectionOverlayBar(GalleryType.trash, _selectedFiles)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
@ -13,6 +13,7 @@ import 'package:photos/core/event_bus.dart';
|
|||
import 'package:photos/db/files_db.dart';
|
||||
import 'package:photos/events/collection_updated_event.dart';
|
||||
import 'package:photos/events/files_updated_event.dart';
|
||||
import "package:photos/events/force_reload_trash_page_event.dart";
|
||||
import 'package:photos/events/local_photos_updated_event.dart';
|
||||
import 'package:photos/models/file.dart';
|
||||
import 'package:photos/models/selected_files.dart';
|
||||
|
@ -264,6 +265,9 @@ Future<bool> deleteFromTrash(BuildContext context, List<File> files) async {
|
|||
source: "deleteFromTrash",
|
||||
),
|
||||
);
|
||||
//the FilesUpdateEvent is not reloading trash on premanently removing
|
||||
//files, so need to fire ForceReloadTrashPageEvent
|
||||
Bus.instance.fire(ForceReloadTrashPageEvent());
|
||||
} catch (e, s) {
|
||||
_logger.info("failed to delete from trash", e, s);
|
||||
rethrow;
|
||||
|
|
|
@ -43,14 +43,15 @@ Future<ButtonResult?> showErrorDialogForException({
|
|||
required BuildContext context,
|
||||
required Exception exception,
|
||||
bool isDismissible = true,
|
||||
String apiErrorPrefix = "It looks like something went wrong.",
|
||||
}) async {
|
||||
String errorMessage =
|
||||
"It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.";
|
||||
if (exception is DioError &&
|
||||
exception.response != null &&
|
||||
exception.response!.data["code"] != null) {
|
||||
errorMessage = "It looks like something went wrong. \n\nReason: " +
|
||||
exception.response!.data["code"];
|
||||
errorMessage =
|
||||
"$apiErrorPrefix\n\nReason: " + exception.response!.data["code"];
|
||||
}
|
||||
return showDialogWidget(
|
||||
context: context,
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'dart:math';
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:connectivity/connectivity.dart';
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_sodium/flutter_sodium.dart';
|
||||
|
@ -282,9 +282,11 @@ class FileUploader {
|
|||
return;
|
||||
}
|
||||
final connectivityResult = await (Connectivity().checkConnectivity());
|
||||
final canUploadUnderCurrentNetworkConditions =
|
||||
(connectivityResult == ConnectivityResult.wifi ||
|
||||
Configuration.instance.shouldBackupOverMobileData());
|
||||
bool canUploadUnderCurrentNetworkConditions = true;
|
||||
if (connectivityResult == ConnectivityResult.mobile) {
|
||||
canUploadUnderCurrentNetworkConditions =
|
||||
Configuration.instance.shouldBackupOverMobileData();
|
||||
}
|
||||
if (!canUploadUnderCurrentNetworkConditions) {
|
||||
throw WiFiUnavailableError();
|
||||
}
|
||||
|
|
16
lib/utils/intent_util.dart
Normal file
|
@ -0,0 +1,16 @@
|
|||
import "package:flutter/services.dart";
|
||||
import "package:media_extension/media_extension.dart";
|
||||
import "package:media_extension/media_extension_action_types.dart";
|
||||
|
||||
Future<MediaExtentionAction> initIntentAction() async {
|
||||
final mediaExtensionPlugin = MediaExtension();
|
||||
MediaExtentionAction mediaExtensionAction;
|
||||
try {
|
||||
mediaExtensionAction = await mediaExtensionPlugin.getIntentAction();
|
||||
} on PlatformException {
|
||||
mediaExtensionAction = MediaExtentionAction(action: IntentAction.main);
|
||||
} catch (error) {
|
||||
mediaExtensionAction = MediaExtentionAction(action: IntentAction.main);
|
||||
}
|
||||
return mediaExtensionAction;
|
||||
}
|
48
pubspec.lock
|
@ -208,38 +208,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.0"
|
||||
connectivity:
|
||||
connectivity_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: connectivity
|
||||
sha256: a8e91263cf3e25fb5cc95e19dfde4999e32a648ac3b9e8a558a28165731678f8
|
||||
name: connectivity_plus
|
||||
sha256: "8875e8ed511a49f030e313656154e4bbbcef18d68dfd32eb853fac10bce48e96"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
connectivity_for_web:
|
||||
version: "3.0.3"
|
||||
connectivity_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: connectivity_for_web
|
||||
sha256: "01a390c1d5adc2ed1fa1f52d120c07fe9fd01166a93f965a832fd6cfc0ea6482"
|
||||
name: connectivity_plus_platform_interface
|
||||
sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.0+1"
|
||||
connectivity_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: connectivity_macos
|
||||
sha256: "51ae08d5162eca9669b9d8951ed83ce19c5355a81149f94e4dee2740beb93628"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1+2"
|
||||
connectivity_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: connectivity_platform_interface
|
||||
sha256: "2d82e942df9d49f29a24bb07fb5ce085d4a53e47818c62364d2b6deb9e0d7a8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "1.2.4"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -493,6 +477,14 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_animate:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_animate
|
||||
sha256: c6e38af9fe376fa07fb6995fe6a12d07a08828d2847aa652b7c1bc60f9993374
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
flutter_blurhash:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1005,6 +997,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
nm:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: nm
|
||||
sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
node_preamble:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
15
pubspec.yaml
|
@ -12,7 +12,7 @@ description: ente photos application
|
|||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
|
||||
version: 0.7.26+426
|
||||
version: 0.7.27+427
|
||||
|
||||
environment:
|
||||
sdk: '>=2.17.0 <3.0.0'
|
||||
|
@ -30,7 +30,7 @@ dependencies:
|
|||
collection: # dart
|
||||
computer: ^2.0.0
|
||||
confetti: ^0.6.0
|
||||
connectivity: ^3.0.3
|
||||
connectivity_plus: ^3.0.3
|
||||
cupertino_icons: ^1.0.0
|
||||
device_info: ^2.0.2
|
||||
dio: ^4.0.6
|
||||
|
@ -49,6 +49,7 @@ dependencies:
|
|||
fk_user_agent: ^2.0.1
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_animate: ^4.1.0
|
||||
flutter_cache_manager: ^3.3.0
|
||||
flutter_datetime_picker: ^1.5.1
|
||||
flutter_easyloading: ^3.0.0
|
||||
|
@ -76,7 +77,7 @@ dependencies:
|
|||
local_auth: ^1.1.5
|
||||
logging: ^1.0.1
|
||||
lottie: ^1.2.2
|
||||
media_extension: ^1.0.0
|
||||
media_extension: ^1.0.1
|
||||
modal_bottom_sheet: ^3.0.0-pre
|
||||
motionphoto:
|
||||
git: "https://github.com/ente-io/motionphoto.git"
|
||||
|
@ -147,6 +148,14 @@ flutter_native_splash:
|
|||
android_fullscreen: true
|
||||
android_gravity: center
|
||||
ios_content_mode: center
|
||||
android_12:
|
||||
# The image parameter sets the splash screen icon image. If this parameter is not specified,
|
||||
# the app's launcher icon will be used instead.
|
||||
# Please note that the splash screen will be clipped to a circle on the center of the screen.
|
||||
# App icon without an icon background: This should be 1152×1152 pixels, and fit within a circle
|
||||
# 768 pixels in diameter.
|
||||
image: assets/splash-screen-light.png
|
||||
image_dark: assets/splash-screen-dark.png
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
|
14
run.sh
|
@ -2,14 +2,12 @@
|
|||
|
||||
FLUTTER_RUN="flutter run --flavor dev "
|
||||
|
||||
if [ ! -z "$1" ]
|
||||
then
|
||||
SUPPLIED_ENV_FILE="$1"
|
||||
while IFS= read -r line
|
||||
do
|
||||
FLUTTER_RUN="$FLUTTER_RUN --dart-define $line"
|
||||
SUPPLIED_ENV_FILE=".env"
|
||||
while IFS= read -r line
|
||||
do
|
||||
FLUTTER_RUN="$FLUTTER_RUN --dart-define $line"
|
||||
|
||||
done < "$SUPPLIED_ENV_FILE"
|
||||
|
||||
done < "$SUPPLIED_ENV_FILE"
|
||||
fi
|
||||
echo "Running: $FLUTTER_RUN"
|
||||
$FLUTTER_RUN
|
||||
|
|