NullSafety: Apply dart migrate suggestions + compilation fixes
This commit is contained in:
parent
0e62793b7b
commit
d6ad004354
89 changed files with 1232 additions and 1241 deletions
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
|
@ -23,7 +23,7 @@ class EnteApp extends StatefulWidget {
|
|||
const EnteApp(
|
||||
this.runBackgroundTask,
|
||||
this.killBackgroundTask, {
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
|
|
@ -614,7 +614,7 @@ class Configuration {
|
|||
return _preferences.setBool(keyShouldHideFromRecents, value);
|
||||
}
|
||||
|
||||
void setVolatilePassword(String volatilePassword) {
|
||||
void setVolatilePassword(String? volatilePassword) {
|
||||
_volatilePassword = volatilePassword;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
@ -230,7 +230,7 @@ Future<bool> _isRunningInForeground() async {
|
|||
(currentTime - kFGTaskDeathTimeoutInMicroseconds);
|
||||
}
|
||||
|
||||
Future<void> _killBGTask([String taskId]) async {
|
||||
Future<void> _killBGTask([String? taskId]) async {
|
||||
await UploadLocksDB.instance.releaseLocksAcquiredByOwnerBefore(
|
||||
ProcessType.background.toString(),
|
||||
DateTime.now().microsecondsSinceEpoch,
|
||||
|
@ -281,7 +281,7 @@ Future<void> _logFGHeartBeatInfo() async {
|
|||
_logger.info('isAlreaduunningFG: $isRunningInFG, last Beat: $lastRun');
|
||||
}
|
||||
|
||||
void _scheduleSuicide(Duration duration, [String taskID]) {
|
||||
void _scheduleSuicide(Duration duration, [String? taskID]) {
|
||||
final taskIDVal = taskID ?? 'no taskID';
|
||||
_logger.warning("Schedule seppuku taskID: $taskIDVal");
|
||||
Future.delayed(duration, () {
|
||||
|
|
|
@ -16,9 +16,9 @@ class LocalAuthenticationService {
|
|||
String infoMessage,
|
||||
) async {
|
||||
if (await _isLocalAuthSupportedOnDevice()) {
|
||||
AppLock.of(context).setEnabled(false);
|
||||
AppLock.of(context)!.setEnabled(false);
|
||||
final result = await requestAuthentication(infoMessage);
|
||||
AppLock.of(context).setEnabled(
|
||||
AppLock.of(context)!.setEnabled(
|
||||
Configuration.instance.shouldShowLockScreen(),
|
||||
);
|
||||
if (!result) {
|
||||
|
@ -39,17 +39,17 @@ class LocalAuthenticationService {
|
|||
String errorDialogTitle = "",
|
||||
]) async {
|
||||
if (await _isLocalAuthSupportedOnDevice()) {
|
||||
AppLock.of(context).disable();
|
||||
AppLock.of(context)!.disable();
|
||||
final result = await requestAuthentication(
|
||||
infoMessage,
|
||||
);
|
||||
if (result) {
|
||||
AppLock.of(context).setEnabled(shouldEnableLockScreen);
|
||||
AppLock.of(context)!.setEnabled(shouldEnableLockScreen);
|
||||
await Configuration.instance
|
||||
.setShouldShowLockScreen(shouldEnableLockScreen);
|
||||
return true;
|
||||
} else {
|
||||
AppLock.of(context)
|
||||
AppLock.of(context)!
|
||||
.setEnabled(Configuration.instance.shouldShowLockScreen());
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:email_validator/email_validator.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
|
@ -13,7 +13,7 @@ import 'package:photos/ui/common/web_page.dart';
|
|||
import 'package:step_progress_indicator/step_progress_indicator.dart';
|
||||
|
||||
class EmailEntryPage extends StatefulWidget {
|
||||
const EmailEntryPage({Key key}) : super(key: key);
|
||||
const EmailEntryPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<EmailEntryPage> createState() => _EmailEntryPageState();
|
||||
|
@ -28,13 +28,13 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
|
|||
final _passwordController2 = TextEditingController();
|
||||
final Color _validFieldValueColor = const Color.fromRGBO(45, 194, 98, 0.2);
|
||||
|
||||
String _email;
|
||||
String _password;
|
||||
String? _email;
|
||||
String? _password;
|
||||
String _cnfPassword = '';
|
||||
double _passwordStrength = 0.0;
|
||||
bool _emailIsValid = false;
|
||||
bool _hasAgreedToTOS = true;
|
||||
bool _hasAgreedToE2E = false;
|
||||
bool? _hasAgreedToTOS = true;
|
||||
bool? _hasAgreedToE2E = false;
|
||||
bool _password1Visible = false;
|
||||
bool _password2Visible = false;
|
||||
bool _passwordsMatch = false;
|
||||
|
@ -65,7 +65,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
|
|||
Widget build(BuildContext context) {
|
||||
final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100;
|
||||
|
||||
FloatingActionButtonLocation fabLocation() {
|
||||
FloatingActionButtonLocation? fabLocation() {
|
||||
if (isKeypadOpen) {
|
||||
return null;
|
||||
} else {
|
||||
|
@ -104,9 +104,9 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
|
|||
buttonText: 'Create account',
|
||||
onPressedFunction: () {
|
||||
_config.setVolatilePassword(_passwordController1.text);
|
||||
UserService.instance.setEmail(_email);
|
||||
UserService.instance.setEmail(_email!);
|
||||
UserService.instance
|
||||
.sendOtt(context, _email, isCreateAccountScreen: true);
|
||||
.sendOtt(context, _email!, isCreateAccountScreen: true);
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
),
|
||||
|
@ -162,7 +162,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
|
|||
size: 20,
|
||||
color: Theme.of(context)
|
||||
.inputDecorationTheme
|
||||
.focusedBorder
|
||||
.focusedBorder!
|
||||
.borderSide
|
||||
.color,
|
||||
)
|
||||
|
@ -170,9 +170,9 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
|
|||
),
|
||||
onChanged: (value) {
|
||||
_email = value.trim();
|
||||
if (_emailIsValid != EmailValidator.validate(_email)) {
|
||||
if (_emailIsValid != EmailValidator.validate(_email!)) {
|
||||
setState(() {
|
||||
_emailIsValid = EmailValidator.validate(_email);
|
||||
_emailIsValid = EmailValidator.validate(_email!);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -220,7 +220,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
|
|||
Icons.check,
|
||||
color: Theme.of(context)
|
||||
.inputDecorationTheme
|
||||
.focusedBorder
|
||||
.focusedBorder!
|
||||
.borderSide
|
||||
.color,
|
||||
)
|
||||
|
@ -287,7 +287,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
|
|||
Icons.check,
|
||||
color: Theme.of(context)
|
||||
.inputDecorationTheme
|
||||
.focusedBorder
|
||||
.focusedBorder!
|
||||
.borderSide
|
||||
.color,
|
||||
)
|
||||
|
@ -352,7 +352,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
|
|||
return GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_hasAgreedToTOS = !_hasAgreedToTOS;
|
||||
_hasAgreedToTOS = !_hasAgreedToTOS!;
|
||||
});
|
||||
},
|
||||
behavior: HitTestBehavior.translucent,
|
||||
|
@ -416,7 +416,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
|
|||
],
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.subtitle1
|
||||
.subtitle1!
|
||||
.copyWith(fontSize: 12),
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
|
@ -431,7 +431,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
|
|||
return GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_hasAgreedToE2E = !_hasAgreedToE2E;
|
||||
_hasAgreedToE2E = !_hasAgreedToE2E!;
|
||||
});
|
||||
},
|
||||
behavior: HitTestBehavior.translucent,
|
||||
|
@ -477,7 +477,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
|
|||
],
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.subtitle1
|
||||
.subtitle1!
|
||||
.copyWith(fontSize: 12),
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
|
@ -491,8 +491,8 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
|
|||
bool _isFormValid() {
|
||||
return _emailIsValid &&
|
||||
_passwordsMatch &&
|
||||
_hasAgreedToTOS &&
|
||||
_hasAgreedToE2E &&
|
||||
_hasAgreedToTOS! &&
|
||||
_hasAgreedToE2E! &&
|
||||
_passwordIsValid;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:email_validator/email_validator.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
|
@ -9,7 +9,7 @@ import 'package:photos/ui/common/dynamic_fab.dart';
|
|||
import 'package:photos/ui/common/web_page.dart';
|
||||
|
||||
class LoginPage extends StatefulWidget {
|
||||
const LoginPage({Key key}) : super(key: key);
|
||||
const LoginPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<LoginPage> createState() => _LoginPageState();
|
||||
|
@ -18,8 +18,8 @@ class LoginPage extends StatefulWidget {
|
|||
class _LoginPageState extends State<LoginPage> {
|
||||
final _config = Configuration.instance;
|
||||
bool _emailIsValid = false;
|
||||
String _email;
|
||||
Color _emailInputFieldColor;
|
||||
String? _email;
|
||||
Color? _emailInputFieldColor;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -31,7 +31,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||
Widget build(BuildContext context) {
|
||||
final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100;
|
||||
|
||||
FloatingActionButtonLocation fabLocation() {
|
||||
FloatingActionButtonLocation? fabLocation() {
|
||||
if (isKeypadOpen) {
|
||||
return null;
|
||||
} else {
|
||||
|
@ -57,9 +57,9 @@ class _LoginPageState extends State<LoginPage> {
|
|||
isFormValid: _emailIsValid,
|
||||
buttonText: 'Log in',
|
||||
onPressedFunction: () {
|
||||
UserService.instance.setEmail(_email);
|
||||
UserService.instance.setEmail(_email!);
|
||||
UserService.instance
|
||||
.sendOtt(context, _email, isCreateAccountScreen: false);
|
||||
.sendOtt(context, _email!, isCreateAccountScreen: false);
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
),
|
||||
|
@ -105,7 +105,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||
size: 20,
|
||||
color: Theme.of(context)
|
||||
.inputDecorationTheme
|
||||
.focusedBorder
|
||||
.focusedBorder!
|
||||
.borderSide
|
||||
.color,
|
||||
)
|
||||
|
@ -114,7 +114,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||
onChanged: (value) {
|
||||
setState(() {
|
||||
_email = value.trim();
|
||||
_emailIsValid = EmailValidator.validate(_email);
|
||||
_emailIsValid = EmailValidator.validate(_email!);
|
||||
if (_emailIsValid) {
|
||||
_emailInputFieldColor =
|
||||
const Color.fromRGBO(45, 194, 98, 0.2);
|
||||
|
@ -145,7 +145,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||
text: TextSpan(
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.subtitle1
|
||||
.subtitle1!
|
||||
.copyWith(fontSize: 12),
|
||||
children: [
|
||||
const TextSpan(
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/ente_theme_data.dart';
|
||||
|
@ -15,7 +15,7 @@ class OTTVerificationPage extends StatefulWidget {
|
|||
this.email, {
|
||||
this.isChangeEmail = false,
|
||||
this.isCreateAccountScreen = false,
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -29,7 +29,7 @@ class _OTTVerificationPageState extends State<OTTVerificationPage> {
|
|||
Widget build(BuildContext context) {
|
||||
final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100;
|
||||
|
||||
FloatingActionButtonLocation fabLocation() {
|
||||
FloatingActionButtonLocation? fabLocation() {
|
||||
if (isKeypadOpen) {
|
||||
return null;
|
||||
} else {
|
||||
|
@ -114,7 +114,7 @@ class _OTTVerificationPageState extends State<OTTVerificationPage> {
|
|||
text: TextSpan(
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.subtitle1
|
||||
.subtitle1!
|
||||
.copyWith(fontSize: 14),
|
||||
children: [
|
||||
const TextSpan(text: "We've sent a mail to "),
|
||||
|
@ -134,7 +134,7 @@ class _OTTVerificationPageState extends State<OTTVerificationPage> {
|
|||
'Please check your inbox (and spam) to complete verification',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.subtitle1
|
||||
.subtitle1!
|
||||
.copyWith(fontSize: 14),
|
||||
),
|
||||
],
|
||||
|
@ -187,7 +187,7 @@ class _OTTVerificationPageState extends State<OTTVerificationPage> {
|
|||
},
|
||||
child: Text(
|
||||
"Resend email",
|
||||
style: Theme.of(context).textTheme.subtitle1.copyWith(
|
||||
style: Theme.of(context).textTheme.subtitle1!.copyWith(
|
||||
fontSize: 14,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
@ -26,7 +26,7 @@ enum PasswordEntryMode {
|
|||
class PasswordEntryPage extends StatefulWidget {
|
||||
final PasswordEntryMode mode;
|
||||
|
||||
const PasswordEntryPage({this.mode = PasswordEntryMode.set, Key key})
|
||||
const PasswordEntryPage({this.mode = PasswordEntryMode.set, Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -41,7 +41,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
|
|||
final _passwordController1 = TextEditingController(),
|
||||
_passwordController2 = TextEditingController();
|
||||
final Color _validFieldValueColor = const Color.fromRGBO(45, 194, 98, 0.2);
|
||||
String _volatilePassword;
|
||||
String? _volatilePassword;
|
||||
String _passwordInInputBox = '';
|
||||
String _passwordInInputConfirmationBox = '';
|
||||
double _passwordStrength = 0.0;
|
||||
|
@ -62,7 +62,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
|
|||
if (_volatilePassword != null) {
|
||||
Future.delayed(
|
||||
Duration.zero,
|
||||
() => _showRecoveryCodeDialog(_volatilePassword),
|
||||
() => _showRecoveryCodeDialog(_volatilePassword!),
|
||||
);
|
||||
}
|
||||
_password1FocusNode.addListener(() {
|
||||
|
@ -81,7 +81,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
|
|||
Widget build(BuildContext context) {
|
||||
final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100;
|
||||
|
||||
FloatingActionButtonLocation fabLocation() {
|
||||
FloatingActionButtonLocation? fabLocation() {
|
||||
if (isKeypadOpen) {
|
||||
return null;
|
||||
} else {
|
||||
|
@ -167,7 +167,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
|
|||
textAlign: TextAlign.start,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.subtitle1
|
||||
.subtitle1!
|
||||
.copyWith(fontSize: 14),
|
||||
),
|
||||
),
|
||||
|
@ -178,7 +178,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
|
|||
text: TextSpan(
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.subtitle1
|
||||
.subtitle1!
|
||||
.copyWith(fontSize: 14),
|
||||
children: [
|
||||
const TextSpan(
|
||||
|
@ -187,7 +187,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
|
|||
),
|
||||
TextSpan(
|
||||
text: "we cannot decrypt your data",
|
||||
style: Theme.of(context).textTheme.subtitle1.copyWith(
|
||||
style: Theme.of(context).textTheme.subtitle1!.copyWith(
|
||||
fontSize: 14,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
|
@ -245,7 +245,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
|
|||
Icons.check,
|
||||
color: Theme.of(context)
|
||||
.inputDecorationTheme
|
||||
.focusedBorder
|
||||
.focusedBorder!
|
||||
.borderSide
|
||||
.color,
|
||||
)
|
||||
|
@ -307,7 +307,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
|
|||
Icons.check,
|
||||
color: Theme.of(context)
|
||||
.inputDecorationTheme
|
||||
.focusedBorder
|
||||
.focusedBorder!
|
||||
.borderSide
|
||||
.color,
|
||||
)
|
||||
|
@ -364,7 +364,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
|
|||
child: RichText(
|
||||
text: TextSpan(
|
||||
text: "How it works",
|
||||
style: Theme.of(context).textTheme.subtitle1.copyWith(
|
||||
style: Theme.of(context).textTheme.subtitle1!.copyWith(
|
||||
fontSize: 14,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
|
@ -17,7 +17,7 @@ import 'package:photos/utils/dialog_util.dart';
|
|||
import 'package:photos/utils/email_util.dart';
|
||||
|
||||
class PasswordReentryPage extends StatefulWidget {
|
||||
const PasswordReentryPage({Key key}) : super(key: key);
|
||||
const PasswordReentryPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<PasswordReentryPage> createState() => _PasswordReentryPageState();
|
||||
|
@ -27,7 +27,7 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
|
|||
final _logger = Logger((_PasswordReentryPageState).toString());
|
||||
final _passwordController = TextEditingController();
|
||||
final FocusNode _passwordFocusNode = FocusNode();
|
||||
String email;
|
||||
String? email;
|
||||
bool _passwordInFocus = false;
|
||||
bool _passwordVisible = false;
|
||||
|
||||
|
@ -46,7 +46,7 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
|
|||
Widget build(BuildContext context) {
|
||||
final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100;
|
||||
|
||||
FloatingActionButtonLocation fabLocation() {
|
||||
FloatingActionButtonLocation? fabLocation() {
|
||||
if (isKeypadOpen) {
|
||||
return null;
|
||||
} else {
|
||||
|
@ -78,7 +78,7 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
|
|||
try {
|
||||
await Configuration.instance.decryptAndSaveSecrets(
|
||||
_passwordController.text,
|
||||
Configuration.instance.getKeyAttributes(),
|
||||
Configuration.instance.getKeyAttributes()!,
|
||||
);
|
||||
} on KeyDerivationError catch (e, s) {
|
||||
_logger.severe("Password verification failed", e, s);
|
||||
|
@ -245,7 +245,7 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
|
|||
child: Text(
|
||||
"Forgot password",
|
||||
style:
|
||||
Theme.of(context).textTheme.subtitle1.copyWith(
|
||||
Theme.of(context).textTheme.subtitle1!.copyWith(
|
||||
fontSize: 14,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
|
@ -267,7 +267,7 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
|
|||
child: Text(
|
||||
"Change email",
|
||||
style:
|
||||
Theme.of(context).textTheme.subtitle1.copyWith(
|
||||
Theme.of(context).textTheme.subtitle1!.copyWith(
|
||||
fontSize: 14,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:io' as io;
|
||||
|
||||
|
@ -15,20 +15,20 @@ import 'package:share_plus/share_plus.dart';
|
|||
import 'package:step_progress_indicator/step_progress_indicator.dart';
|
||||
|
||||
class RecoveryKeyPage extends StatefulWidget {
|
||||
final bool showAppBar;
|
||||
final bool? showAppBar;
|
||||
final String recoveryKey;
|
||||
final String doneText;
|
||||
final Function() onDone;
|
||||
final bool isDismissible;
|
||||
final String title;
|
||||
final String text;
|
||||
final String subText;
|
||||
final Function()? onDone;
|
||||
final bool? isDismissible;
|
||||
final String? title;
|
||||
final String? text;
|
||||
final String? subText;
|
||||
final bool showProgressBar;
|
||||
|
||||
const RecoveryKeyPage(
|
||||
this.recoveryKey,
|
||||
this.doneText, {
|
||||
Key key,
|
||||
Key? key,
|
||||
this.showAppBar,
|
||||
this.onDone,
|
||||
this.isDismissible,
|
||||
|
@ -56,7 +56,7 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
|
|||
'recovery code should have $mnemonicKeyWordCount words',
|
||||
);
|
||||
}
|
||||
final double topPadding = widget.showAppBar
|
||||
final double topPadding = widget.showAppBar!
|
||||
? 40
|
||||
: widget.showProgressBar
|
||||
? 32
|
||||
|
@ -79,7 +79,7 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
|
|||
),
|
||||
),
|
||||
)
|
||||
: widget.showAppBar
|
||||
: widget.showAppBar!
|
||||
? AppBar(
|
||||
elevation: 0,
|
||||
title: Text(widget.title ?? "Recovery key"),
|
||||
|
@ -100,14 +100,14 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
|
|||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
widget.showAppBar
|
||||
widget.showAppBar!
|
||||
? const SizedBox.shrink()
|
||||
: Text(
|
||||
widget.title ?? "Recovery key",
|
||||
style: Theme.of(context).textTheme.headline4,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(widget.showAppBar ? 0 : 12),
|
||||
padding: EdgeInsets.all(widget.showAppBar! ? 0 : 12),
|
||||
),
|
||||
Text(
|
||||
widget.text ??
|
||||
|
@ -263,6 +263,6 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
|
|||
if (_recoveryKeyFile.existsSync()) {
|
||||
await _recoveryKeyFile.delete();
|
||||
}
|
||||
widget.onDone();
|
||||
widget.onDone!();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:ui';
|
||||
|
||||
|
@ -10,7 +10,7 @@ import 'package:photos/utils/dialog_util.dart';
|
|||
import 'package:photos/utils/toast_util.dart';
|
||||
|
||||
class RecoveryPage extends StatefulWidget {
|
||||
const RecoveryPage({Key key}) : super(key: key);
|
||||
const RecoveryPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<RecoveryPage> createState() => _RecoveryPageState();
|
||||
|
@ -22,7 +22,7 @@ class _RecoveryPageState extends State<RecoveryPage> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100;
|
||||
FloatingActionButtonLocation fabLocation() {
|
||||
FloatingActionButtonLocation? fabLocation() {
|
||||
if (isKeypadOpen) {
|
||||
return null;
|
||||
} else {
|
||||
|
@ -140,7 +140,7 @@ class _RecoveryPageState extends State<RecoveryPage> {
|
|||
child: Text(
|
||||
"No recovery key?",
|
||||
style:
|
||||
Theme.of(context).textTheme.subtitle1.copyWith(
|
||||
Theme.of(context).textTheme.subtitle1!.copyWith(
|
||||
fontSize: 14,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// @dart=2.9
|
||||
|
||||
import 'dart:io';
|
||||
import 'dart:ui';
|
||||
|
||||
|
@ -24,9 +22,9 @@ class BackupFolderSelectionPage extends StatefulWidget {
|
|||
final String buttonText;
|
||||
|
||||
const BackupFolderSelectionPage({
|
||||
@required this.buttonText,
|
||||
required this.buttonText,
|
||||
this.isOnboarding = false,
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -38,8 +36,8 @@ class _BackupFolderSelectionPageState extends State<BackupFolderSelectionPage> {
|
|||
final Logger _logger = Logger((_BackupFolderSelectionPageState).toString());
|
||||
final Set<String> _allDevicePathIDs = <String>{};
|
||||
final Set<String> _selectedDevicePathIDs = <String>{};
|
||||
List<DeviceCollection> _deviceCollections;
|
||||
Map<String, int> _pathIDToItemCount;
|
||||
List<DeviceCollection>? _deviceCollections;
|
||||
Map<String, int>? _pathIDToItemCount;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -50,10 +48,10 @@ class _BackupFolderSelectionPageState extends State<BackupFolderSelectionPage> {
|
|||
await FilesDB.instance.getDevicePathIDToImportedFileCount();
|
||||
setState(() {
|
||||
_deviceCollections = files;
|
||||
_deviceCollections.sort((first, second) {
|
||||
_deviceCollections!.sort((first, second) {
|
||||
return first.name.toLowerCase().compareTo(second.name.toLowerCase());
|
||||
});
|
||||
for (final file in _deviceCollections) {
|
||||
for (final file in _deviceCollections!) {
|
||||
_allDevicePathIDs.add(file.id);
|
||||
if (file.shouldBackup) {
|
||||
_selectedDevicePathIDs.add(file.id);
|
||||
|
@ -103,7 +101,7 @@ class _BackupFolderSelectionPageState extends State<BackupFolderSelectionPage> {
|
|||
padding: const EdgeInsets.only(left: 24, right: 48),
|
||||
child: Text(
|
||||
"Selected folders will be encrypted and backed up",
|
||||
style: Theme.of(context).textTheme.caption.copyWith(height: 1.3),
|
||||
style: Theme.of(context).textTheme.caption!.copyWith(height: 1.3),
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
|
@ -139,7 +137,7 @@ class _BackupFolderSelectionPageState extends State<BackupFolderSelectionPage> {
|
|||
} else {
|
||||
_selectedDevicePathIDs.addAll(_allDevicePathIDs);
|
||||
}
|
||||
_deviceCollections.sort((first, second) {
|
||||
_deviceCollections!.sort((first, second) {
|
||||
return first.name
|
||||
.toLowerCase()
|
||||
.compareTo(second.name.toLowerCase());
|
||||
|
@ -191,7 +189,7 @@ class _BackupFolderSelectionPageState extends State<BackupFolderSelectionPage> {
|
|||
},
|
||||
child: Text(
|
||||
"Skip",
|
||||
style: Theme.of(context).textTheme.caption.copyWith(
|
||||
style: Theme.of(context).textTheme.caption!.copyWith(
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
|
@ -247,11 +245,11 @@ class _BackupFolderSelectionPageState extends State<BackupFolderSelectionPage> {
|
|||
padding: const EdgeInsets.only(right: 4),
|
||||
child: ImplicitlyAnimatedReorderableList<DeviceCollection>(
|
||||
controller: scrollController,
|
||||
items: _deviceCollections,
|
||||
items: _deviceCollections!,
|
||||
areItemsTheSame: (oldItem, newItem) => oldItem.id == newItem.id,
|
||||
onReorderFinished: (item, from, to, newItems) {
|
||||
setState(() {
|
||||
_deviceCollections
|
||||
_deviceCollections!
|
||||
..clear()
|
||||
..addAll(newItems);
|
||||
});
|
||||
|
@ -261,7 +259,7 @@ class _BackupFolderSelectionPageState extends State<BackupFolderSelectionPage> {
|
|||
key: ValueKey(file),
|
||||
builder: (context, dragAnimation, inDrag) {
|
||||
final t = dragAnimation.value;
|
||||
final elevation = lerpDouble(0, 8, t);
|
||||
final elevation = lerpDouble(0, 8, t)!;
|
||||
final themeColor = Theme.of(context).colorScheme.onSurface;
|
||||
final color =
|
||||
Color.lerp(themeColor, themeColor.withOpacity(0.8), t);
|
||||
|
@ -288,7 +286,7 @@ class _BackupFolderSelectionPageState extends State<BackupFolderSelectionPage> {
|
|||
Widget _getFileItem(DeviceCollection deviceCollection) {
|
||||
final isSelected = _selectedDevicePathIDs.contains(deviceCollection.id);
|
||||
final importedCount = _pathIDToItemCount != null
|
||||
? _pathIDToItemCount[deviceCollection.id] ?? 0
|
||||
? _pathIDToItemCount![deviceCollection.id] ?? 0
|
||||
: -1;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 1, right: 1),
|
||||
|
@ -326,7 +324,7 @@ class _BackupFolderSelectionPageState extends State<BackupFolderSelectionPage> {
|
|||
activeColor: Colors.white,
|
||||
value: isSelected,
|
||||
onChanged: (value) {
|
||||
if (value) {
|
||||
if (value!) {
|
||||
_selectedDevicePathIDs.add(deviceCollection.id);
|
||||
} else {
|
||||
_selectedDevicePathIDs.remove(deviceCollection.id);
|
||||
|
@ -375,7 +373,7 @@ class _BackupFolderSelectionPageState extends State<BackupFolderSelectionPage> {
|
|||
),
|
||||
],
|
||||
),
|
||||
_getThumbnail(deviceCollection.thumbnail, isSelected),
|
||||
_getThumbnail(deviceCollection.thumbnail!, isSelected),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
|
@ -393,7 +391,7 @@ class _BackupFolderSelectionPageState extends State<BackupFolderSelectionPage> {
|
|||
}
|
||||
|
||||
void _sortFiles() {
|
||||
_deviceCollections.sort((first, second) {
|
||||
_deviceCollections!.sort((first, second) {
|
||||
if (_selectedDevicePathIDs.contains(first.id) &&
|
||||
_selectedDevicePathIDs.contains(second.id)) {
|
||||
return first.name.toLowerCase().compareTo(second.name.toLowerCase());
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
|
@ -17,7 +17,7 @@ import 'package:photos/ui/viewer/gallery/empty_state.dart';
|
|||
|
||||
class DeviceFoldersGridViewWidget extends StatefulWidget {
|
||||
const DeviceFoldersGridViewWidget({
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -27,8 +27,8 @@ class DeviceFoldersGridViewWidget extends StatefulWidget {
|
|||
|
||||
class _DeviceFoldersGridViewWidgetState
|
||||
extends State<DeviceFoldersGridViewWidget> {
|
||||
StreamSubscription<BackupFoldersUpdatedEvent> _backupFoldersUpdatedEvent;
|
||||
StreamSubscription<LocalPhotosUpdatedEvent> _localFilesSubscription;
|
||||
StreamSubscription<BackupFoldersUpdatedEvent>? _backupFoldersUpdatedEvent;
|
||||
StreamSubscription<LocalPhotosUpdatedEvent>? _localFilesSubscription;
|
||||
String _loadReason = "init";
|
||||
|
||||
@override
|
||||
|
@ -65,7 +65,7 @@ class _DeviceFoldersGridViewWidgetState
|
|||
.getDeviceCollections(includeCoverThumbnail: true),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return snapshot.data.isEmpty
|
||||
return snapshot.data!.isEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.all(22),
|
||||
child: (isMigrationDone
|
||||
|
@ -81,10 +81,10 @@ class _DeviceFoldersGridViewWidgetState
|
|||
physics: const ScrollPhysics(),
|
||||
// to disable GridView's scrolling
|
||||
itemBuilder: (context, index) {
|
||||
final deviceCollection = snapshot.data[index];
|
||||
final deviceCollection = snapshot.data![index];
|
||||
return DeviceFolderIcon(deviceCollection);
|
||||
},
|
||||
itemCount: snapshot.data.length,
|
||||
itemCount: snapshot.data!.length,
|
||||
);
|
||||
} else if (snapshot.hasError) {
|
||||
logger.severe("failed to load device gallery", snapshot.error);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/services/local_authentication_service.dart';
|
||||
|
@ -10,7 +10,7 @@ class HiddenCollectionsButtonWidget extends StatelessWidget {
|
|||
|
||||
const HiddenCollectionsButtonWidget(
|
||||
this.textStyle, {
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -24,7 +24,7 @@ class HiddenCollectionsButtonWidget extends StatelessWidget {
|
|||
padding: const EdgeInsets.all(0),
|
||||
side: BorderSide(
|
||||
width: 0.5,
|
||||
color: Theme.of(context).iconTheme.color.withOpacity(0.24),
|
||||
color: Theme.of(context).iconTheme.color!.withOpacity(0.24),
|
||||
),
|
||||
),
|
||||
child: SizedBox(
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:math';
|
||||
|
||||
|
@ -17,11 +17,11 @@ class RemoteCollectionsGridViewWidget extends StatelessWidget {
|
|||
static const fixedGapBetweenAlbum = 8.0;
|
||||
static const minGapForHorizontalPadding = 8.0;
|
||||
|
||||
final List<CollectionWithThumbnail> collections;
|
||||
final List<CollectionWithThumbnail>? collections;
|
||||
|
||||
const RemoteCollectionsGridViewWidget(
|
||||
this.collections, {
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -46,13 +46,13 @@ class RemoteCollectionsGridViewWidget extends StatelessWidget {
|
|||
physics: const ScrollPhysics(),
|
||||
// to disable GridView's scrolling
|
||||
itemBuilder: (context, index) {
|
||||
if (index < collections.length) {
|
||||
return CollectionItem(collections[index], sideOfThumbnail);
|
||||
if (index < collections!.length) {
|
||||
return CollectionItem(collections![index], sideOfThumbnail);
|
||||
} else {
|
||||
return const CreateNewAlbumWidget();
|
||||
}
|
||||
},
|
||||
itemCount: collections.length + 1,
|
||||
itemCount: collections!.length + 1,
|
||||
// To include the + button
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: albumsCountInOneRow,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
|
@ -26,7 +26,7 @@ import 'package:photos/ui/viewer/gallery/empty_state.dart';
|
|||
import 'package:photos/utils/local_settings.dart';
|
||||
|
||||
class CollectionsGalleryWidget extends StatefulWidget {
|
||||
const CollectionsGalleryWidget({Key key}) : super(key: key);
|
||||
const CollectionsGalleryWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<CollectionsGalleryWidget> createState() =>
|
||||
|
@ -36,10 +36,10 @@ class CollectionsGalleryWidget extends StatefulWidget {
|
|||
class _CollectionsGalleryWidgetState extends State<CollectionsGalleryWidget>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
final _logger = Logger((_CollectionsGalleryWidgetState).toString());
|
||||
StreamSubscription<LocalPhotosUpdatedEvent> _localFilesSubscription;
|
||||
StreamSubscription<CollectionUpdatedEvent> _collectionUpdatesSubscription;
|
||||
StreamSubscription<UserLoggedOutEvent> _loggedOutEvent;
|
||||
AlbumSortKey sortKey;
|
||||
late StreamSubscription<LocalPhotosUpdatedEvent> _localFilesSubscription;
|
||||
late StreamSubscription<CollectionUpdatedEvent> _collectionUpdatesSubscription;
|
||||
late StreamSubscription<UserLoggedOutEvent> _loggedOutEvent;
|
||||
AlbumSortKey? sortKey;
|
||||
String _loadReason = "init";
|
||||
|
||||
@override
|
||||
|
@ -98,8 +98,8 @@ class _CollectionsGalleryWidgetState extends State<CollectionsGalleryWidget>
|
|||
(first, second) {
|
||||
if (sortKey == AlbumSortKey.albumName) {
|
||||
return compareAsciiLowerCaseNatural(
|
||||
first.collection.name,
|
||||
second.collection.name,
|
||||
first.collection.name!,
|
||||
second.collection.name!,
|
||||
);
|
||||
} else if (sortKey == AlbumSortKey.newestPhoto) {
|
||||
return (second.thumbnail?.creationTime ?? -1 * intMaxValue)
|
||||
|
@ -121,13 +121,13 @@ class _CollectionsGalleryWidgetState extends State<CollectionsGalleryWidget>
|
|||
}
|
||||
|
||||
Widget _getCollectionsGalleryWidget(
|
||||
List<CollectionWithThumbnail> collections,
|
||||
List<CollectionWithThumbnail>? collections,
|
||||
) {
|
||||
final TextStyle trashAndHiddenTextStyle = Theme.of(context)
|
||||
.textTheme
|
||||
.subtitle1
|
||||
.subtitle1!
|
||||
.copyWith(
|
||||
color: Theme.of(context).textTheme.subtitle1.color.withOpacity(0.5),
|
||||
color: Theme.of(context).textTheme.subtitle1!.color!.withOpacity(0.5),
|
||||
);
|
||||
|
||||
return SingleChildScrollView(
|
||||
|
@ -190,9 +190,9 @@ class _CollectionsGalleryWidgetState extends State<CollectionsGalleryWidget>
|
|||
}
|
||||
return Text(
|
||||
text,
|
||||
style: Theme.of(context).textTheme.subtitle1.copyWith(
|
||||
style: Theme.of(context).textTheme.subtitle1!.copyWith(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).iconTheme.color.withOpacity(0.7),
|
||||
color: Theme.of(context).iconTheme.color!.withOpacity(0.7),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -228,7 +228,7 @@ class _CollectionsGalleryWidgetState extends State<CollectionsGalleryWidget>
|
|||
),
|
||||
onSelected: (int index) async {
|
||||
sortKey = AlbumSortKey.values[index];
|
||||
await LocalSettings.instance.setAlbumSortKey(sortKey);
|
||||
await LocalSettings.instance.setAlbumSortKey(sortKey!);
|
||||
setState(() {});
|
||||
},
|
||||
itemBuilder: (context) {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DividerWithPadding extends StatelessWidget {
|
||||
final double left, top, right, bottom, thinckness;
|
||||
const DividerWithPadding({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.left = 0,
|
||||
this.top = 0,
|
||||
this.right = 0,
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class BottomShadowWidget extends StatelessWidget {
|
||||
final double offsetDy;
|
||||
final Color shadowColor;
|
||||
const BottomShadowWidget({this.offsetDy = 28, this.shadowColor, Key key})
|
||||
final Color? shadowColor;
|
||||
const BottomShadowWidget({this.offsetDy = 28, this.shadowColor, Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/ente_theme_data.dart';
|
||||
|
@ -11,14 +11,14 @@ enum ActionType {
|
|||
}
|
||||
|
||||
// if dialog is dismissed by tapping outside, this will return null
|
||||
Future<DialogUserChoice> showChoiceDialog<T>(
|
||||
Future<DialogUserChoice?> showChoiceDialog<T>(
|
||||
BuildContext context,
|
||||
String title,
|
||||
String content, {
|
||||
String firstAction = 'Ok',
|
||||
Color firstActionColor,
|
||||
Color? firstActionColor,
|
||||
String secondAction = 'Cancel',
|
||||
Color secondActionColor,
|
||||
Color? secondActionColor,
|
||||
ActionType actionType = ActionType.confirm,
|
||||
}) {
|
||||
final AlertDialog alert = AlertDialog(
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:math' as math;
|
||||
|
||||
|
@ -6,13 +6,13 @@ import 'package:flutter/material.dart';
|
|||
import 'package:photos/ente_theme_data.dart';
|
||||
|
||||
class DynamicFAB extends StatelessWidget {
|
||||
final bool isKeypadOpen;
|
||||
final bool isFormValid;
|
||||
final String buttonText;
|
||||
final Function onPressedFunction;
|
||||
final bool? isKeypadOpen;
|
||||
final bool? isFormValid;
|
||||
final String? buttonText;
|
||||
final Function? onPressedFunction;
|
||||
|
||||
const DynamicFAB({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.isKeypadOpen,
|
||||
this.buttonText,
|
||||
this.isFormValid,
|
||||
|
@ -21,7 +21,7 @@ class DynamicFAB extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (isKeypadOpen) {
|
||||
if (isKeypadOpen!) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: [
|
||||
|
@ -43,13 +43,13 @@ class DynamicFAB extends StatelessWidget {
|
|||
Theme.of(context).colorScheme.dynamicFABBackgroundColor,
|
||||
foregroundColor:
|
||||
Theme.of(context).colorScheme.dynamicFABTextColor,
|
||||
onPressed: isFormValid
|
||||
? onPressedFunction
|
||||
onPressed: isFormValid!
|
||||
? onPressedFunction as void Function()?
|
||||
: () {
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
child: Transform.rotate(
|
||||
angle: isFormValid ? 0 : math.pi / 2,
|
||||
angle: isFormValid! ? 0 : math.pi / 2,
|
||||
child: const Icon(
|
||||
Icons.chevron_right,
|
||||
size: 36,
|
||||
|
@ -65,8 +65,8 @@ class DynamicFAB extends StatelessWidget {
|
|||
height: 56,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: OutlinedButton(
|
||||
onPressed: isFormValid ? onPressedFunction : null,
|
||||
child: Text(buttonText),
|
||||
onPressed: isFormValid! ? onPressedFunction as void Function()? : null,
|
||||
child: Text(buttonText!),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -75,17 +75,17 @@ class DynamicFAB extends StatelessWidget {
|
|||
|
||||
class NoScalingAnimation extends FloatingActionButtonAnimator {
|
||||
@override
|
||||
Offset getOffset({Offset begin, Offset end, double progress}) {
|
||||
Offset getOffset({Offset? begin, required Offset end, double? progress}) {
|
||||
return end;
|
||||
}
|
||||
|
||||
@override
|
||||
Animation<double> getRotationAnimation({Animation<double> parent}) {
|
||||
Animation<double> getRotationAnimation({required Animation<double> parent}) {
|
||||
return Tween<double>(begin: 1.0, end: 1.0).animate(parent);
|
||||
}
|
||||
|
||||
@override
|
||||
Animation<double> getScaleAnimation({Animation<double> parent}) {
|
||||
Animation<double> getScaleAnimation({required Animation<double> parent}) {
|
||||
return Tween<double>(begin: 1.0, end: 1.0).animate(parent);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/theme/ente_theme.dart';
|
||||
|
||||
class GradientButton extends StatelessWidget {
|
||||
final List<Color> linearGradientColors;
|
||||
final Function onTap;
|
||||
final Function? onTap;
|
||||
|
||||
// text is ignored if child is specified
|
||||
final String text;
|
||||
|
||||
// nullable
|
||||
final IconData iconData;
|
||||
final IconData? iconData;
|
||||
|
||||
// padding between the text and icon
|
||||
final double paddingValue;
|
||||
|
||||
const GradientButton({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.linearGradientColors = const [
|
||||
Color(0xFF2CD267),
|
||||
Color(0xFF1DB954),
|
||||
|
@ -65,7 +65,7 @@ class GradientButton extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
onTap: onTap as void Function()?,
|
||||
child: Container(
|
||||
height: 56,
|
||||
decoration: BoxDecoration(
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/ente_theme_data.dart';
|
||||
|
@ -6,14 +6,14 @@ import 'package:photos/ente_theme_data.dart';
|
|||
class LinearProgressDialog extends StatefulWidget {
|
||||
final String message;
|
||||
|
||||
const LinearProgressDialog(this.message, {Key key}) : super(key: key);
|
||||
const LinearProgressDialog(this.message, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
LinearProgressDialogState createState() => LinearProgressDialogState();
|
||||
}
|
||||
|
||||
class LinearProgressDialogState extends State<LinearProgressDialog> {
|
||||
double _progress;
|
||||
double? _progress;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// @dart=2.9
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum ProgressDialogType { normal, download }
|
||||
|
@ -7,7 +5,7 @@ enum ProgressDialogType { normal, download }
|
|||
String _dialogMessage = "Loading...";
|
||||
double _progress = 0.0, _maxProgress = 100.0;
|
||||
|
||||
Widget _customBody;
|
||||
Widget? _customBody;
|
||||
|
||||
TextAlign _textAlign = TextAlign.left;
|
||||
Alignment _progressWidgetAlignment = Alignment.centerLeft;
|
||||
|
@ -15,10 +13,10 @@ Alignment _progressWidgetAlignment = Alignment.centerLeft;
|
|||
TextDirection _direction = TextDirection.ltr;
|
||||
|
||||
bool _isShowing = false;
|
||||
BuildContext _context, _dismissingContext;
|
||||
ProgressDialogType _progressDialogType;
|
||||
BuildContext? _context, _dismissingContext;
|
||||
ProgressDialogType? _progressDialogType;
|
||||
bool _barrierDismissible = true, _showLogs = false;
|
||||
Color _barrierColor;
|
||||
Color? _barrierColor;
|
||||
|
||||
TextStyle _progressTextStyle = const TextStyle(
|
||||
color: Colors.black,
|
||||
|
@ -42,16 +40,16 @@ Widget _progressWidget = Image.asset(
|
|||
);
|
||||
|
||||
class ProgressDialog {
|
||||
_Body _dialog;
|
||||
_Body? _dialog;
|
||||
|
||||
ProgressDialog(
|
||||
BuildContext context, {
|
||||
ProgressDialogType type,
|
||||
bool isDismissible,
|
||||
bool showLogs,
|
||||
TextDirection textDirection,
|
||||
Widget customBody,
|
||||
Color barrierColor,
|
||||
ProgressDialogType? type,
|
||||
bool? isDismissible,
|
||||
bool? showLogs,
|
||||
TextDirection? textDirection,
|
||||
Widget? customBody,
|
||||
Color? barrierColor,
|
||||
}) {
|
||||
_context = context;
|
||||
_progressDialogType = type ?? ProgressDialogType.normal;
|
||||
|
@ -63,20 +61,20 @@ class ProgressDialog {
|
|||
}
|
||||
|
||||
void style({
|
||||
Widget child,
|
||||
double progress,
|
||||
double maxProgress,
|
||||
String message,
|
||||
Widget progressWidget,
|
||||
Color backgroundColor,
|
||||
TextStyle progressTextStyle,
|
||||
TextStyle messageTextStyle,
|
||||
double elevation,
|
||||
TextAlign textAlign,
|
||||
double borderRadius,
|
||||
Curve insetAnimCurve,
|
||||
EdgeInsets padding,
|
||||
Alignment progressWidgetAlignment,
|
||||
Widget? child,
|
||||
double? progress,
|
||||
double? maxProgress,
|
||||
String? message,
|
||||
Widget? progressWidget,
|
||||
Color? backgroundColor,
|
||||
TextStyle? progressTextStyle,
|
||||
TextStyle? messageTextStyle,
|
||||
double? elevation,
|
||||
TextAlign? textAlign,
|
||||
double? borderRadius,
|
||||
Curve? insetAnimCurve,
|
||||
EdgeInsets? padding,
|
||||
Alignment? progressWidgetAlignment,
|
||||
}) {
|
||||
if (_isShowing) return;
|
||||
if (_progressDialogType == ProgressDialogType.download) {
|
||||
|
@ -100,12 +98,12 @@ class ProgressDialog {
|
|||
}
|
||||
|
||||
void update({
|
||||
double progress,
|
||||
double maxProgress,
|
||||
String message,
|
||||
Widget progressWidget,
|
||||
TextStyle progressTextStyle,
|
||||
TextStyle messageTextStyle,
|
||||
double? progress,
|
||||
double? maxProgress,
|
||||
String? message,
|
||||
Widget? progressWidget,
|
||||
TextStyle? progressTextStyle,
|
||||
TextStyle? messageTextStyle,
|
||||
}) {
|
||||
if (_progressDialogType == ProgressDialogType.download) {
|
||||
_progress = progress ?? _progress;
|
||||
|
@ -117,7 +115,7 @@ class ProgressDialog {
|
|||
_messageStyle = messageTextStyle ?? _messageStyle;
|
||||
_progressTextStyle = progressTextStyle ?? _progressTextStyle;
|
||||
|
||||
if (_isShowing) _dialog.update();
|
||||
if (_isShowing) _dialog!.update();
|
||||
}
|
||||
|
||||
bool isShowing() {
|
||||
|
@ -128,7 +126,9 @@ class ProgressDialog {
|
|||
try {
|
||||
if (_isShowing) {
|
||||
_isShowing = false;
|
||||
Navigator.of(_dismissingContext).pop();
|
||||
if (_dismissingContext != null) {
|
||||
Navigator.of(_dismissingContext!).pop();
|
||||
}
|
||||
if (_showLogs) debugPrint('ProgressDialog dismissed');
|
||||
return Future.value(true);
|
||||
} else {
|
||||
|
@ -147,7 +147,7 @@ class ProgressDialog {
|
|||
if (!_isShowing) {
|
||||
_dialog = _Body();
|
||||
showDialog<dynamic>(
|
||||
context: _context,
|
||||
context: _context!,
|
||||
barrierDismissible: _barrierDismissible,
|
||||
barrierColor: _barrierColor,
|
||||
builder: (BuildContext context) {
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/utils/dialog_util.dart';
|
||||
|
||||
class RenameDialog extends StatefulWidget {
|
||||
final String name;
|
||||
final String? name;
|
||||
final String type;
|
||||
final int maxLength;
|
||||
|
||||
const RenameDialog(this.name, this.type, {Key key, this.maxLength = 100})
|
||||
const RenameDialog(this.name, this.type, {Key? key, this.maxLength = 100})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -16,7 +16,7 @@ class RenameDialog extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _RenameDialogState extends State<RenameDialog> {
|
||||
String _newName;
|
||||
String? _newName;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -74,7 +74,7 @@ class _RenameDialogState extends State<RenameDialog> {
|
|||
),
|
||||
),
|
||||
onPressed: () {
|
||||
if (_newName.trim().isEmpty) {
|
||||
if (_newName!.trim().isEmpty) {
|
||||
showErrorDialog(
|
||||
context,
|
||||
"Empty name",
|
||||
|
@ -82,7 +82,7 @@ class _RenameDialogState extends State<RenameDialog> {
|
|||
);
|
||||
return;
|
||||
}
|
||||
if (_newName.trim().length > widget.maxLength) {
|
||||
if (_newName!.trim().length > widget.maxLength) {
|
||||
showErrorDialog(
|
||||
context,
|
||||
"Name too large",
|
||||
|
@ -90,7 +90,7 @@ class _RenameDialogState extends State<RenameDialog> {
|
|||
);
|
||||
return;
|
||||
}
|
||||
Navigator.of(context).pop(_newName.trim());
|
||||
Navigator.of(context).pop(_newName!.trim());
|
||||
},
|
||||
),
|
||||
],
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
|
@ -8,7 +8,7 @@ class WebPage extends StatefulWidget {
|
|||
final String title;
|
||||
final String url;
|
||||
|
||||
const WebPage(this.title, this.url, {Key key}) : super(key: key);
|
||||
const WebPage(this.title, this.url, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<WebPage> createState() => _WebPageState();
|
||||
|
|
|
@ -103,12 +103,12 @@ class BottomActionBarWidget extends StatelessWidget {
|
|||
}
|
||||
|
||||
List<Widget> _iconButtons(BuildContext context) {
|
||||
final iconButtonsWithExpansionIcon = <Widget?>[
|
||||
final iconButtonsWithExpansionIcon = <Widget>[
|
||||
...?iconButtons,
|
||||
ExpansionIconWidget(expandableController: _expandableController)
|
||||
];
|
||||
iconButtonsWithExpansionIcon.removeWhere((element) => element == null);
|
||||
return iconButtonsWithExpansionIcon as List<Widget>;
|
||||
return iconButtonsWithExpansionIcon;
|
||||
}
|
||||
|
||||
ExpandableThemeData _getExpandableTheme() {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// @dart=2.9
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
@ -46,14 +44,14 @@ String _actionName(CollectionActionType type, bool plural) {
|
|||
}
|
||||
|
||||
class CreateCollectionPage extends StatefulWidget {
|
||||
final SelectedFiles selectedFiles;
|
||||
final List<SharedMediaFile> sharedFiles;
|
||||
final SelectedFiles? selectedFiles;
|
||||
final List<SharedMediaFile>? sharedFiles;
|
||||
final CollectionActionType actionType;
|
||||
|
||||
const CreateCollectionPage(
|
||||
this.selectedFiles,
|
||||
this.sharedFiles, {
|
||||
Key key,
|
||||
Key? key,
|
||||
this.actionType = CollectionActionType.addFiles,
|
||||
}) : super(key: key);
|
||||
|
||||
|
@ -63,13 +61,13 @@ class CreateCollectionPage extends StatefulWidget {
|
|||
|
||||
class _CreateCollectionPageState extends State<CreateCollectionPage> {
|
||||
final _logger = Logger((_CreateCollectionPageState).toString());
|
||||
String _albumName;
|
||||
late String _albumName;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final filesCount = widget.sharedFiles != null
|
||||
? widget.sharedFiles.length
|
||||
: widget.selectedFiles.files.length;
|
||||
? widget.sharedFiles!.length
|
||||
: widget.selectedFiles!.files.length;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(_actionName(widget.actionType, filesCount > 1)),
|
||||
|
@ -134,9 +132,9 @@ class _CreateCollectionPageState extends State<CreateCollectionPage> {
|
|||
} else if (snapshot.hasData) {
|
||||
return ListView.builder(
|
||||
itemBuilder: (context, index) {
|
||||
return _buildCollectionItem(snapshot.data[index]);
|
||||
return _buildCollectionItem(snapshot.data![index]);
|
||||
},
|
||||
itemCount: snapshot.data.length,
|
||||
itemCount: snapshot.data!.length,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
);
|
||||
|
@ -171,7 +169,7 @@ class _CreateCollectionPageState extends State<CreateCollectionPage> {
|
|||
const Padding(padding: EdgeInsets.all(8)),
|
||||
Expanded(
|
||||
child: Text(
|
||||
item.collection.name,
|
||||
item.collection.name!,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
|
@ -184,8 +182,8 @@ class _CreateCollectionPageState extends State<CreateCollectionPage> {
|
|||
showShortToast(
|
||||
context,
|
||||
widget.actionType == CollectionActionType.addFiles
|
||||
? "Added successfully to " + item.collection.name
|
||||
: "Moved successfully to " + item.collection.name,
|
||||
? "Added successfully to " + item.collection.name!
|
||||
: "Moved successfully to " + item.collection.name!,
|
||||
);
|
||||
_navigateToCollection(item.collection);
|
||||
}
|
||||
|
@ -305,11 +303,11 @@ class _CreateCollectionPageState extends State<CreateCollectionPage> {
|
|||
await dialog.show();
|
||||
try {
|
||||
final int fromCollectionID =
|
||||
widget.selectedFiles.files.first?.collectionID;
|
||||
widget.selectedFiles!.files.first.collectionID!;
|
||||
await CollectionsService.instance.move(
|
||||
toCollectionID,
|
||||
fromCollectionID,
|
||||
widget.selectedFiles.files?.toList(),
|
||||
widget.selectedFiles!.files?.toList() ?? <File>[],
|
||||
);
|
||||
await dialog.hide();
|
||||
RemoteSyncService.instance.sync(silently: true);
|
||||
|
@ -318,7 +316,7 @@ class _CreateCollectionPageState extends State<CreateCollectionPage> {
|
|||
return true;
|
||||
} on AssertionError catch (e) {
|
||||
await dialog.hide();
|
||||
showErrorDialog(context, "Oops", e.message);
|
||||
showErrorDialog(context, "Oops", e.message as String?);
|
||||
return false;
|
||||
} catch (e, s) {
|
||||
_logger.severe("Could not move to album", e, s);
|
||||
|
@ -332,15 +330,15 @@ class _CreateCollectionPageState extends State<CreateCollectionPage> {
|
|||
final dialog = createProgressDialog(context, "Restoring files...");
|
||||
await dialog.show();
|
||||
try {
|
||||
await CollectionsService.instance
|
||||
.restore(toCollectionID, widget.selectedFiles.files?.toList());
|
||||
await CollectionsService.instance.restore(
|
||||
toCollectionID, widget.selectedFiles!.files?.toList() ?? <File>[]);
|
||||
RemoteSyncService.instance.sync(silently: true);
|
||||
widget.selectedFiles?.clearAll();
|
||||
await dialog.hide();
|
||||
return true;
|
||||
} on AssertionError catch (e) {
|
||||
await dialog.hide();
|
||||
showErrorDialog(context, "Oops", e.message);
|
||||
showErrorDialog(context, "Oops", e.message as String?);
|
||||
return false;
|
||||
} catch (e, s) {
|
||||
_logger.severe("Could not move to album", e, s);
|
||||
|
@ -359,13 +357,18 @@ class _CreateCollectionPageState extends State<CreateCollectionPage> {
|
|||
if (widget.sharedFiles != null) {
|
||||
filesPendingUpload.addAll(
|
||||
await convertIncomingSharedMediaToFile(
|
||||
widget.sharedFiles,
|
||||
widget.sharedFiles!,
|
||||
collectionID,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
for (final file in widget.selectedFiles.files) {
|
||||
final currentFile = await FilesDB.instance.getFile(file.generatedID);
|
||||
for (final file in widget.selectedFiles!.files) {
|
||||
final File? currentFile =
|
||||
await (FilesDB.instance.getFile(file.generatedID!));
|
||||
if (currentFile == null) {
|
||||
_logger.severe("Failed to find fileBy genID");
|
||||
continue;
|
||||
}
|
||||
if (currentFile.uploadedFileID == null) {
|
||||
currentFile.collectionID = collectionID;
|
||||
filesPendingUpload.add(currentFile);
|
||||
|
@ -396,8 +399,8 @@ class _CreateCollectionPageState extends State<CreateCollectionPage> {
|
|||
return false;
|
||||
}
|
||||
|
||||
Future<Collection> _createAlbum(String albumName) async {
|
||||
Collection collection;
|
||||
Future<Collection?> _createAlbum(String albumName) async {
|
||||
Collection? collection;
|
||||
final dialog = createProgressDialog(context, "Creating album...");
|
||||
await dialog.show();
|
||||
try {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
|
@ -49,10 +49,10 @@ class ExtentsPageView extends StatefulWidget {
|
|||
/// child that could possibly be displayed in the page view, instead of just
|
||||
/// those children that are actually visible.
|
||||
ExtentsPageView({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.scrollDirection = Axis.horizontal,
|
||||
this.reverse = false,
|
||||
PageController controller,
|
||||
PageController? controller,
|
||||
this.physics,
|
||||
this.pageSnapping = true,
|
||||
this.onPageChanged,
|
||||
|
@ -81,15 +81,15 @@ class ExtentsPageView extends StatefulWidget {
|
|||
/// you are planning to change child order at a later time, consider using
|
||||
/// [PageView] or [PageView.custom].
|
||||
ExtentsPageView.builder({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.scrollDirection = Axis.horizontal,
|
||||
this.reverse = false,
|
||||
PageController controller,
|
||||
PageController? controller,
|
||||
this.physics,
|
||||
this.pageSnapping = true,
|
||||
this.onPageChanged,
|
||||
@required IndexedWidgetBuilder itemBuilder,
|
||||
int itemCount,
|
||||
required IndexedWidgetBuilder itemBuilder,
|
||||
int? itemCount,
|
||||
this.dragStartBehavior = DragStartBehavior.start,
|
||||
this.openDrawer,
|
||||
}) : controller = controller ?? _defaultPageController,
|
||||
|
@ -99,16 +99,16 @@ class ExtentsPageView extends StatefulWidget {
|
|||
super(key: key);
|
||||
|
||||
ExtentsPageView.extents({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.extents = 1,
|
||||
this.scrollDirection = Axis.horizontal,
|
||||
this.reverse = false,
|
||||
PageController controller,
|
||||
PageController? controller,
|
||||
this.physics,
|
||||
this.pageSnapping = true,
|
||||
this.onPageChanged,
|
||||
@required IndexedWidgetBuilder itemBuilder,
|
||||
int itemCount,
|
||||
required IndexedWidgetBuilder itemBuilder,
|
||||
int? itemCount,
|
||||
this.dragStartBehavior = DragStartBehavior.start,
|
||||
this.openDrawer,
|
||||
}) : controller = controller ?? _defaultPageController,
|
||||
|
@ -201,14 +201,14 @@ class ExtentsPageView extends StatefulWidget {
|
|||
/// ```
|
||||
/// {@end-tool}
|
||||
ExtentsPageView.custom({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.scrollDirection = Axis.horizontal,
|
||||
this.reverse = false,
|
||||
PageController controller,
|
||||
PageController? controller,
|
||||
this.physics,
|
||||
this.pageSnapping = true,
|
||||
this.onPageChanged,
|
||||
@required this.childrenDelegate,
|
||||
required this.childrenDelegate,
|
||||
this.dragStartBehavior = DragStartBehavior.start,
|
||||
this.openDrawer,
|
||||
}) : assert(childrenDelegate != null),
|
||||
|
@ -257,13 +257,13 @@ class ExtentsPageView extends StatefulWidget {
|
|||
/// [PageScrollPhysics] prior to being used.
|
||||
///
|
||||
/// Defaults to matching platform conventions.
|
||||
final ScrollPhysics physics;
|
||||
final ScrollPhysics? physics;
|
||||
|
||||
/// Set to false to disable page snapping, useful for custom scroll behavior.
|
||||
final bool pageSnapping;
|
||||
|
||||
/// Called whenever the page in the center of the viewport changes.
|
||||
final ValueChanged<int> onPageChanged;
|
||||
final ValueChanged<int>? onPageChanged;
|
||||
|
||||
/// A delegate that provides the children for the [PageView].
|
||||
///
|
||||
|
@ -276,7 +276,7 @@ class ExtentsPageView extends StatefulWidget {
|
|||
/// {@macro flutter.widgets.scrollable.dragStartBehavior}
|
||||
final DragStartBehavior dragStartBehavior;
|
||||
|
||||
final Function openDrawer; //nullable
|
||||
final Function? openDrawer; //nullable
|
||||
|
||||
@override
|
||||
State<ExtentsPageView> createState() => _PageViewState();
|
||||
|
@ -292,7 +292,7 @@ class _PageViewState extends State<ExtentsPageView> {
|
|||
widget.openDrawer != null
|
||||
? widget.controller.addListener(() {
|
||||
if (widget.controller.offset < -45) {
|
||||
widget.openDrawer();
|
||||
widget.openDrawer!();
|
||||
}
|
||||
})
|
||||
: null;
|
||||
|
@ -304,7 +304,7 @@ class _PageViewState extends State<ExtentsPageView> {
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
AxisDirection _getDirection(BuildContext context) {
|
||||
AxisDirection? _getDirection(BuildContext context) {
|
||||
switch (widget.scrollDirection) {
|
||||
case Axis.horizontal:
|
||||
assert(debugCheckHasDirectionality(context));
|
||||
|
@ -322,8 +322,8 @@ class _PageViewState extends State<ExtentsPageView> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final AxisDirection axisDirection = _getDirection(context);
|
||||
final ScrollPhysics physics = widget.pageSnapping
|
||||
final AxisDirection axisDirection = _getDirection(context)!;
|
||||
final ScrollPhysics? physics = widget.pageSnapping
|
||||
? _kPagePhysics.applyTo(widget.physics)
|
||||
: widget.physics;
|
||||
|
||||
|
@ -332,11 +332,11 @@ class _PageViewState extends State<ExtentsPageView> {
|
|||
if (notification.depth == 0 &&
|
||||
widget.onPageChanged != null &&
|
||||
notification is ScrollUpdateNotification) {
|
||||
final PageMetrics metrics = notification.metrics;
|
||||
final int currentPage = metrics.page.round();
|
||||
final PageMetrics metrics = notification.metrics as PageMetrics;
|
||||
final int currentPage = metrics.page!.round();
|
||||
if (currentPage != _lastReportedPage) {
|
||||
_lastReportedPage = currentPage;
|
||||
widget.onPageChanged(currentPage);
|
||||
widget.onPageChanged!(currentPage);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/core/errors.dart';
|
||||
|
@ -7,9 +7,9 @@ import 'package:photos/ui/payment/subscription.dart';
|
|||
import 'package:photos/utils/email_util.dart';
|
||||
|
||||
class HeaderErrorWidget extends StatelessWidget {
|
||||
final Error _error;
|
||||
final Error? _error;
|
||||
|
||||
const HeaderErrorWidget({Key key, @required Error error})
|
||||
const HeaderErrorWidget({Key? key, required Error? error})
|
||||
: _error = error,
|
||||
super(key: key);
|
||||
|
||||
|
@ -123,7 +123,7 @@ class HeaderErrorWidget extends StatelessWidget {
|
|||
padding: const EdgeInsets.fromLTRB(50, 16, 50, 16),
|
||||
side: BorderSide(
|
||||
width: 2,
|
||||
color: Colors.orange[600],
|
||||
color: Colors.orange[600]!,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
|
@ -16,12 +16,12 @@ import 'package:photos/ui/viewer/actions/file_selection_overlay_bar.dart';
|
|||
import 'package:photos/ui/viewer/gallery/gallery.dart';
|
||||
|
||||
class HomeGalleryWidget extends StatelessWidget {
|
||||
final Widget header;
|
||||
final Widget footer;
|
||||
final SelectedFiles selectedFiles;
|
||||
final Widget? header;
|
||||
final Widget? footer;
|
||||
final SelectedFiles? selectedFiles;
|
||||
|
||||
const HomeGalleryWidget({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.header,
|
||||
this.footer,
|
||||
this.selectedFiles,
|
||||
|
@ -42,7 +42,7 @@ class HomeGalleryWidget extends StatelessWidget {
|
|||
result = await FilesDB.instance.getAllLocalAndUploadedFiles(
|
||||
creationStartTime,
|
||||
creationEndTime,
|
||||
ownerID,
|
||||
ownerID!,
|
||||
limit: limit,
|
||||
asc: asc,
|
||||
ignoredCollectionIDs: collectionsToHide,
|
||||
|
@ -51,7 +51,7 @@ class HomeGalleryWidget extends StatelessWidget {
|
|||
result = await FilesDB.instance.getAllPendingOrUploadedFiles(
|
||||
creationStartTime,
|
||||
creationEndTime,
|
||||
ownerID,
|
||||
ownerID!,
|
||||
limit: limit,
|
||||
asc: asc,
|
||||
ignoredCollectionIDs: collectionsToHide,
|
||||
|
@ -88,7 +88,7 @@ class HomeGalleryWidget extends StatelessWidget {
|
|||
return Stack(
|
||||
children: [
|
||||
gallery,
|
||||
FileSelectionOverlayBar(GalleryType.homepage, selectedFiles)
|
||||
FileSelectionOverlayBar(GalleryType.homepage, selectedFiles!)
|
||||
],
|
||||
);
|
||||
// return gallery;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
|
@ -17,7 +17,7 @@ import 'package:photos/ui/common/gradient_button.dart';
|
|||
import 'package:photos/ui/payment/subscription.dart';
|
||||
|
||||
class LandingPageWidget extends StatefulWidget {
|
||||
const LandingPageWidget({Key key}) : super(key: key);
|
||||
const LandingPageWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<LandingPageWidget> createState() => _LandingPageWidgetState();
|
||||
|
@ -240,7 +240,7 @@ class FeatureItemWidget extends StatelessWidget {
|
|||
this.featureTitleFirstLine,
|
||||
this.featureTitleSecondLine,
|
||||
this.subText, {
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/ente_theme_data.dart';
|
||||
|
@ -14,20 +14,20 @@ import 'package:photos/utils/share_util.dart';
|
|||
import 'package:step_progress_indicator/step_progress_indicator.dart';
|
||||
|
||||
class MemoriesWidget extends StatelessWidget {
|
||||
const MemoriesWidget({Key key}) : super(key: key);
|
||||
const MemoriesWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<List<Memory>>(
|
||||
future: MemoriesService.instance.getMemories(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError || !snapshot.hasData || snapshot.data.isEmpty) {
|
||||
if (snapshot.hasError || !snapshot.hasData || snapshot.data!.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
} else {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildMemories(snapshot.data),
|
||||
_buildMemories(snapshot.data!),
|
||||
const Divider(),
|
||||
],
|
||||
);
|
||||
|
@ -69,17 +69,17 @@ class MemoriesWidget extends StatelessWidget {
|
|||
|
||||
bool _areMemoriesFromSameYear(Memory first, Memory second) {
|
||||
final firstDate =
|
||||
DateTime.fromMicrosecondsSinceEpoch(first.file.creationTime);
|
||||
DateTime.fromMicrosecondsSinceEpoch(first.file.creationTime!);
|
||||
final secondDate =
|
||||
DateTime.fromMicrosecondsSinceEpoch(second.file.creationTime);
|
||||
DateTime.fromMicrosecondsSinceEpoch(second.file.creationTime!);
|
||||
return firstDate.year == secondDate.year;
|
||||
}
|
||||
}
|
||||
|
||||
class MemoryWidget extends StatefulWidget {
|
||||
const MemoryWidget({
|
||||
Key key,
|
||||
@required this.memories,
|
||||
Key? key,
|
||||
required this.memories,
|
||||
}) : super(key: key);
|
||||
|
||||
final List<Memory> memories;
|
||||
|
@ -119,7 +119,7 @@ class _MemoryWidgetState extends State<MemoryWidget> {
|
|||
title,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.subtitle1
|
||||
.subtitle1!
|
||||
.copyWith(fontSize: 12),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
|
@ -186,7 +186,7 @@ class _MemoryWidgetState extends State<MemoryWidget> {
|
|||
|
||||
String _getTitle(Memory memory) {
|
||||
final present = DateTime.now();
|
||||
final then = DateTime.fromMicrosecondsSinceEpoch(memory.file.creationTime);
|
||||
final then = DateTime.fromMicrosecondsSinceEpoch(memory.file.creationTime!);
|
||||
final diffInYears = present.year - then.year;
|
||||
if (diffInYears == 1) {
|
||||
return "1 year ago";
|
||||
|
@ -201,7 +201,7 @@ class FullScreenMemory extends StatefulWidget {
|
|||
final List<Memory> memories;
|
||||
final int index;
|
||||
|
||||
const FullScreenMemory(this.title, this.memories, this.index, {Key key})
|
||||
const FullScreenMemory(this.title, this.memories, this.index, {Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -215,7 +215,7 @@ class _FullScreenMemoryState extends State<FullScreenMemory> {
|
|||
// when the top step indicator isn't visible.
|
||||
bool _showCounter = false;
|
||||
bool _showStepIndicator = true;
|
||||
PageController _pageController;
|
||||
PageController? _pageController;
|
||||
bool _shouldDisableScroll = false;
|
||||
final GlobalKey shareButtonKey = GlobalKey();
|
||||
|
||||
|
@ -273,9 +273,9 @@ class _FullScreenMemoryState extends State<FullScreenMemory> {
|
|||
),
|
||||
Text(
|
||||
getFormattedDate(
|
||||
DateTime.fromMicrosecondsSinceEpoch(file.creationTime),
|
||||
DateTime.fromMicrosecondsSinceEpoch(file.creationTime!),
|
||||
),
|
||||
style: Theme.of(context).textTheme.subtitle1.copyWith(
|
||||
style: Theme.of(context).textTheme.subtitle1!.copyWith(
|
||||
fontSize: 14,
|
||||
color: Colors.white,
|
||||
), //same for both themes
|
||||
|
@ -328,7 +328,7 @@ class _FullScreenMemoryState extends State<FullScreenMemory> {
|
|||
'${_index + 1}/${widget.memories.length}',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyText1
|
||||
.bodyText1!
|
||||
.copyWith(color: Colors.white.withOpacity(0.4)),
|
||||
)
|
||||
: AnimatedOpacity(
|
||||
|
@ -338,7 +338,7 @@ class _FullScreenMemoryState extends State<FullScreenMemory> {
|
|||
widget.title,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.headline4
|
||||
.headline4!
|
||||
.copyWith(color: Colors.white),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
|
@ -19,18 +19,18 @@ import 'package:photos/utils/navigation_util.dart';
|
|||
const double kContainerHeight = 36;
|
||||
|
||||
class StatusBarWidget extends StatefulWidget {
|
||||
const StatusBarWidget({Key key}) : super(key: key);
|
||||
const StatusBarWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatusBarWidget> createState() => _StatusBarWidgetState();
|
||||
}
|
||||
|
||||
class _StatusBarWidgetState extends State<StatusBarWidget> {
|
||||
StreamSubscription<SyncStatusUpdate> _subscription;
|
||||
StreamSubscription<NotificationEvent> _notificationSubscription;
|
||||
late StreamSubscription<SyncStatusUpdate> _subscription;
|
||||
late StreamSubscription<NotificationEvent> _notificationSubscription;
|
||||
bool _showStatus = false;
|
||||
bool _showErrorBanner = false;
|
||||
Error _syncError;
|
||||
Error? _syncError;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -118,7 +118,7 @@ class _StatusBarWidgetState extends State<StatusBarWidget> {
|
|||
}
|
||||
|
||||
class SyncStatusWidget extends StatefulWidget {
|
||||
const SyncStatusWidget({Key key}) : super(key: key);
|
||||
const SyncStatusWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<SyncStatusWidget> createState() => _SyncStatusWidgetState();
|
||||
|
@ -127,8 +127,8 @@ class SyncStatusWidget extends StatefulWidget {
|
|||
class _SyncStatusWidgetState extends State<SyncStatusWidget> {
|
||||
static const Duration kSleepDuration = Duration(milliseconds: 3000);
|
||||
|
||||
SyncStatusUpdate _event;
|
||||
StreamSubscription<SyncStatusUpdate> _subscription;
|
||||
SyncStatusUpdate? _event;
|
||||
late StreamSubscription<SyncStatusUpdate> _subscription;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -150,17 +150,17 @@ class _SyncStatusWidgetState extends State<SyncStatusWidget> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool isNotOutdatedEvent = _event != null &&
|
||||
(_event.status == SyncStatus.completedBackup ||
|
||||
_event.status == SyncStatus.completedFirstGalleryImport) &&
|
||||
(DateTime.now().microsecondsSinceEpoch - _event.timestamp >
|
||||
(_event!.status == SyncStatus.completedBackup ||
|
||||
_event!.status == SyncStatus.completedFirstGalleryImport) &&
|
||||
(DateTime.now().microsecondsSinceEpoch - _event!.timestamp >
|
||||
kSleepDuration.inMicroseconds);
|
||||
if (_event == null ||
|
||||
isNotOutdatedEvent ||
|
||||
//sync error cases are handled in StatusBarWidget
|
||||
_event.status == SyncStatus.error) {
|
||||
_event!.status == SyncStatus.error) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
if (_event.status == SyncStatus.completedBackup) {
|
||||
if (_event!.status == SyncStatus.completedBackup) {
|
||||
return const SyncStatusCompletedWidget();
|
||||
}
|
||||
return RefreshIndicatorWidget(_event);
|
||||
|
@ -173,9 +173,9 @@ class RefreshIndicatorWidget extends StatelessWidget {
|
|||
valueColor: AlwaysStoppedAnimation<Color>(Color.fromRGBO(45, 194, 98, 1.0)),
|
||||
);
|
||||
|
||||
final SyncStatusUpdate event;
|
||||
final SyncStatusUpdate? event;
|
||||
|
||||
const RefreshIndicatorWidget(this.event, {Key key}) : super(key: key);
|
||||
const RefreshIndicatorWidget(this.event, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -211,30 +211,30 @@ class RefreshIndicatorWidget extends StatelessWidget {
|
|||
}
|
||||
|
||||
String _getRefreshingText() {
|
||||
if (event.status == SyncStatus.startedFirstGalleryImport ||
|
||||
event.status == SyncStatus.completedFirstGalleryImport) {
|
||||
if (event!.status == SyncStatus.startedFirstGalleryImport ||
|
||||
event!.status == SyncStatus.completedFirstGalleryImport) {
|
||||
return "Loading gallery...";
|
||||
}
|
||||
if (event.status == SyncStatus.applyingRemoteDiff) {
|
||||
if (event!.status == SyncStatus.applyingRemoteDiff) {
|
||||
return "Syncing...";
|
||||
}
|
||||
if (event.status == SyncStatus.preparingForUpload) {
|
||||
if (event!.status == SyncStatus.preparingForUpload) {
|
||||
return "Encrypting backup...";
|
||||
}
|
||||
if (event.status == SyncStatus.inProgress) {
|
||||
return event.completed.toString() +
|
||||
if (event!.status == SyncStatus.inProgress) {
|
||||
return event!.completed.toString() +
|
||||
"/" +
|
||||
event.total.toString() +
|
||||
event!.total.toString() +
|
||||
" memories preserved";
|
||||
}
|
||||
if (event.status == SyncStatus.paused) {
|
||||
return event.reason;
|
||||
if (event!.status == SyncStatus.paused) {
|
||||
return event!.reason;
|
||||
}
|
||||
if (event.status == SyncStatus.error) {
|
||||
return event.reason ?? "Upload failed";
|
||||
if (event!.status == SyncStatus.error) {
|
||||
return event!.reason ?? "Upload failed";
|
||||
}
|
||||
if (event.status == SyncStatus.completedBackup) {
|
||||
if (event.wasStopped) {
|
||||
if (event!.status == SyncStatus.completedBackup) {
|
||||
if (event!.wasStopped) {
|
||||
return "Sync stopped";
|
||||
}
|
||||
}
|
||||
|
@ -243,7 +243,7 @@ class RefreshIndicatorWidget extends StatelessWidget {
|
|||
}
|
||||
|
||||
class BrandingWidget extends StatelessWidget {
|
||||
const BrandingWidget({Key key}) : super(key: key);
|
||||
const BrandingWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -271,7 +271,7 @@ class BrandingWidget extends StatelessWidget {
|
|||
}
|
||||
|
||||
class SyncStatusCompletedWidget extends StatelessWidget {
|
||||
const SyncStatusCompletedWidget({Key key}) : super(key: key);
|
||||
const SyncStatusCompletedWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
@ -51,7 +51,7 @@ import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
|||
import 'package:uni_links/uni_links.dart';
|
||||
|
||||
class HomeWidget extends StatefulWidget {
|
||||
const HomeWidget({Key key}) : super(key: key);
|
||||
const HomeWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _HomeWidgetState();
|
||||
|
@ -74,17 +74,17 @@ class _HomeWidgetState extends State<HomeWidget> {
|
|||
|
||||
// for receiving media files
|
||||
// ignore: unused_field
|
||||
StreamSubscription _intentDataStreamSubscription;
|
||||
List<SharedMediaFile> _sharedFiles;
|
||||
StreamSubscription? _intentDataStreamSubscription;
|
||||
List<SharedMediaFile>? _sharedFiles;
|
||||
|
||||
StreamSubscription<TabChangedEvent> _tabChangedEventSubscription;
|
||||
StreamSubscription<SubscriptionPurchasedEvent> _subscriptionPurchaseEvent;
|
||||
StreamSubscription<TriggerLogoutEvent> _triggerLogoutEvent;
|
||||
StreamSubscription<UserLoggedOutEvent> _loggedOutEvent;
|
||||
StreamSubscription<PermissionGrantedEvent> _permissionGrantedEvent;
|
||||
StreamSubscription<SyncStatusUpdate> _firstImportEvent;
|
||||
StreamSubscription<BackupFoldersUpdatedEvent> _backupFoldersUpdatedEvent;
|
||||
StreamSubscription<AccountConfiguredEvent> _accountConfiguredEvent;
|
||||
late StreamSubscription<TabChangedEvent> _tabChangedEventSubscription;
|
||||
late StreamSubscription<SubscriptionPurchasedEvent> _subscriptionPurchaseEvent;
|
||||
late StreamSubscription<TriggerLogoutEvent> _triggerLogoutEvent;
|
||||
late StreamSubscription<UserLoggedOutEvent> _loggedOutEvent;
|
||||
late StreamSubscription<PermissionGrantedEvent> _permissionGrantedEvent;
|
||||
late StreamSubscription<SyncStatusUpdate> _firstImportEvent;
|
||||
late StreamSubscription<BackupFoldersUpdatedEvent> _backupFoldersUpdatedEvent;
|
||||
late StreamSubscription<AccountConfiguredEvent> _accountConfiguredEvent;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -323,7 +323,7 @@ class _HomeWidgetState extends State<HomeWidget> {
|
|||
if (UserRemoteFlagService.instance.showPasswordReminder()) {
|
||||
return const PasswordReminder();
|
||||
}
|
||||
if (_sharedFiles != null && _sharedFiles.isNotEmpty) {
|
||||
if (_sharedFiles != null && _sharedFiles!.isNotEmpty) {
|
||||
ReceiveSharingIntent.reset();
|
||||
return CreateCollectionPage(null, _sharedFiles);
|
||||
}
|
||||
|
@ -392,7 +392,7 @@ class _HomeWidgetState extends State<HomeWidget> {
|
|||
Future<bool> _initDeepLinks() async {
|
||||
// Platform messages may fail, so we use a try/catch PlatformException.
|
||||
try {
|
||||
final String initialLink = await getInitialLink();
|
||||
final String? initialLink = await getInitialLink();
|
||||
// Parse the link and warn the user, if it is not correct,
|
||||
// but keep in mind it could be `null`.
|
||||
if (initialLink != null) {
|
||||
|
@ -410,8 +410,8 @@ class _HomeWidgetState extends State<HomeWidget> {
|
|||
|
||||
// Attach a listener to the stream
|
||||
linkStream.listen(
|
||||
(String link) {
|
||||
_logger.info("Link received: " + link);
|
||||
(String? link) {
|
||||
_logger.info("Link received: " + link!);
|
||||
_getCredentials(context, link);
|
||||
},
|
||||
onError: (err) {
|
||||
|
@ -421,11 +421,11 @@ class _HomeWidgetState extends State<HomeWidget> {
|
|||
return false;
|
||||
}
|
||||
|
||||
void _getCredentials(BuildContext context, String link) {
|
||||
void _getCredentials(BuildContext context, String? link) {
|
||||
if (Configuration.instance.hasConfiguredAccount()) {
|
||||
return;
|
||||
}
|
||||
final ott = Uri.parse(link).queryParameters["ott"];
|
||||
final ott = Uri.parse(link!).queryParameters["ott"]!;
|
||||
UserService.instance.verifyEmail(context, ott);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// @dart=2.9
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
@ -12,18 +10,18 @@ class DraggableScrollbar extends StatefulWidget {
|
|||
final Color backgroundColor;
|
||||
final Color drawColor;
|
||||
final double heightScrollThumb;
|
||||
final EdgeInsetsGeometry padding;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
final int totalCount;
|
||||
final int initialScrollIndex;
|
||||
final double bottomSafeArea;
|
||||
final int currentFirstIndex;
|
||||
final ValueChanged<double> onChange;
|
||||
final ValueChanged<double>? onChange;
|
||||
final String Function(int) labelTextBuilder;
|
||||
final bool isEnabled;
|
||||
|
||||
const DraggableScrollbar({
|
||||
Key key,
|
||||
@required this.child,
|
||||
Key? key,
|
||||
required this.child,
|
||||
this.backgroundColor = Colors.white,
|
||||
this.drawColor = Colors.grey,
|
||||
this.heightScrollThumb = 80.0,
|
||||
|
@ -32,7 +30,7 @@ class DraggableScrollbar extends StatefulWidget {
|
|||
this.totalCount = 1,
|
||||
this.initialScrollIndex = 0,
|
||||
this.currentFirstIndex = 0,
|
||||
@required this.labelTextBuilder,
|
||||
required this.labelTextBuilder,
|
||||
this.onChange,
|
||||
this.isEnabled = true,
|
||||
}) : super(key: key);
|
||||
|
@ -47,18 +45,18 @@ class DraggableScrollbarState extends State<DraggableScrollbar>
|
|||
static const labelAnimationDuration = Duration(milliseconds: 1000);
|
||||
double thumbOffset = 0.0;
|
||||
bool isDragging = false;
|
||||
int currentFirstIndex;
|
||||
late int currentFirstIndex;
|
||||
|
||||
double get thumbMin => 0.0;
|
||||
|
||||
double get thumbMax =>
|
||||
context.size.height - widget.heightScrollThumb - widget.bottomSafeArea;
|
||||
context.size!.height - widget.heightScrollThumb - widget.bottomSafeArea;
|
||||
|
||||
AnimationController _thumbAnimationController;
|
||||
Animation<double> _thumbAnimation;
|
||||
AnimationController _labelAnimationController;
|
||||
Animation<double> _labelAnimation;
|
||||
Timer _fadeoutTimer;
|
||||
late AnimationController _thumbAnimationController;
|
||||
Animation<double>? _thumbAnimation;
|
||||
late AnimationController _labelAnimationController;
|
||||
Animation<double>? _labelAnimation;
|
||||
Timer? _fadeoutTimer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -66,7 +64,7 @@ class DraggableScrollbarState extends State<DraggableScrollbar>
|
|||
currentFirstIndex = widget.currentFirstIndex;
|
||||
|
||||
if (widget.initialScrollIndex > 0 && widget.totalCount > 1) {
|
||||
WidgetsBinding.instance?.addPostFrameCallback((_) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
setState(
|
||||
() => thumbOffset = (widget.initialScrollIndex / widget.totalCount) *
|
||||
(thumbMax - thumbMin),
|
||||
|
@ -130,7 +128,7 @@ class DraggableScrollbarState extends State<DraggableScrollbar>
|
|||
}
|
||||
|
||||
Widget buildThumb() => Padding(
|
||||
padding: widget.padding,
|
||||
padding: widget.padding!,
|
||||
child: Container(
|
||||
alignment: Alignment.topRight,
|
||||
margin: EdgeInsets.only(top: thumbOffset),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:math' show max;
|
||||
|
||||
|
@ -17,7 +17,7 @@ typedef HugeListViewErrorBuilder = Widget Function(
|
|||
|
||||
class HugeListView<T> extends StatefulWidget {
|
||||
/// A [ScrollablePositionedList] controller for jumping or scrolling to an item.
|
||||
final ItemScrollController controller;
|
||||
final ItemScrollController? controller;
|
||||
|
||||
/// Index of an item to initially align within the viewport.
|
||||
final int startIndex;
|
||||
|
@ -46,29 +46,29 @@ class HugeListView<T> extends StatefulWidget {
|
|||
final HugeListViewItemBuilder<T> itemBuilder;
|
||||
|
||||
/// Called to build a progress widget while the whole list is initialized.
|
||||
final WidgetBuilder waitBuilder;
|
||||
final WidgetBuilder? waitBuilder;
|
||||
|
||||
/// Called to build a widget when the list is empty.
|
||||
final WidgetBuilder emptyResultBuilder;
|
||||
final WidgetBuilder? emptyResultBuilder;
|
||||
|
||||
/// Called to build a widget when there is an error.
|
||||
final HugeListViewErrorBuilder errorBuilder;
|
||||
final HugeListViewErrorBuilder? errorBuilder;
|
||||
|
||||
/// Event to call with the index of the topmost visible item in the viewport while scrolling.
|
||||
/// Can be used to display the current letter of an alphabetically sorted list, for instance.
|
||||
final ValueChanged<int> firstShown;
|
||||
final ValueChanged<int>? firstShown;
|
||||
|
||||
final bool isDraggableScrollbarEnabled;
|
||||
|
||||
final EdgeInsetsGeometry thumbPadding;
|
||||
final EdgeInsetsGeometry? thumbPadding;
|
||||
|
||||
const HugeListView({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.controller,
|
||||
@required this.startIndex,
|
||||
@required this.totalCount,
|
||||
@required this.labelTextBuilder,
|
||||
@required this.itemBuilder,
|
||||
required this.startIndex,
|
||||
required this.totalCount,
|
||||
required this.labelTextBuilder,
|
||||
required this.itemBuilder,
|
||||
this.waitBuilder,
|
||||
this.emptyResultBuilder,
|
||||
this.errorBuilder,
|
||||
|
@ -121,13 +121,13 @@ class HugeListViewState<T> extends State<HugeListView<T>> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (error != null && widget.errorBuilder != null) {
|
||||
return widget.errorBuilder(context, error);
|
||||
return widget.errorBuilder!(context, error);
|
||||
}
|
||||
if (widget.totalCount == -1 && widget.waitBuilder != null) {
|
||||
return widget.waitBuilder(context);
|
||||
return widget.waitBuilder!(context);
|
||||
}
|
||||
if (widget.totalCount == 0 && widget.emptyResultBuilder != null) {
|
||||
return widget.emptyResultBuilder(context);
|
||||
return widget.emptyResultBuilder!(context);
|
||||
}
|
||||
|
||||
return LayoutBuilder(
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
@ -25,16 +25,16 @@ import 'package:photos/utils/navigation_util.dart';
|
|||
import 'package:visibility_detector/visibility_detector.dart';
|
||||
|
||||
class LazyLoadingGallery extends StatefulWidget {
|
||||
final List<File> files;
|
||||
final List<File?> files;
|
||||
final int index;
|
||||
final Stream<FilesUpdatedEvent> reloadEvent;
|
||||
final Stream<FilesUpdatedEvent>? reloadEvent;
|
||||
final Set<EventType> removalEventTypes;
|
||||
final GalleryLoader asyncLoader;
|
||||
final SelectedFiles selectedFiles;
|
||||
final SelectedFiles? selectedFiles;
|
||||
final String tag;
|
||||
final String logTag;
|
||||
final String? logTag;
|
||||
final Stream<int> currentIndexStream;
|
||||
final int photoGirdSize;
|
||||
final int? photoGirdSize;
|
||||
|
||||
LazyLoadingGallery(
|
||||
this.files,
|
||||
|
@ -47,7 +47,7 @@ class LazyLoadingGallery extends StatefulWidget {
|
|||
this.currentIndexStream, {
|
||||
this.logTag = "",
|
||||
this.photoGirdSize = photoGridSizeDefault,
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key ?? UniqueKey());
|
||||
|
||||
@override
|
||||
|
@ -58,12 +58,12 @@ class _LazyLoadingGalleryState extends State<LazyLoadingGallery> {
|
|||
static const kRecycleLimit = 400;
|
||||
static const kNumberOfDaysToRenderBeforeAndAfter = 8;
|
||||
|
||||
Logger _logger;
|
||||
late Logger _logger;
|
||||
|
||||
List<File> _files;
|
||||
StreamSubscription<FilesUpdatedEvent> _reloadEventSubscription;
|
||||
StreamSubscription<int> _currentIndexSubscription;
|
||||
bool _shouldRender;
|
||||
List<File?>? _files;
|
||||
late StreamSubscription<FilesUpdatedEvent> _reloadEventSubscription;
|
||||
late StreamSubscription<int> _currentIndexSubscription;
|
||||
bool? _shouldRender;
|
||||
final ValueNotifier<bool> _toggleSelectAllFromDay = ValueNotifier(false);
|
||||
final ValueNotifier<bool> _showSelectAllButton = ValueNotifier(false);
|
||||
final ValueNotifier<bool> _areAllFromDaySelected = ValueNotifier(false);
|
||||
|
@ -71,7 +71,7 @@ class _LazyLoadingGalleryState extends State<LazyLoadingGallery> {
|
|||
@override
|
||||
void initState() {
|
||||
//this is for removing the 'select all from day' icon on unselecting all files with 'cancel'
|
||||
widget.selectedFiles.addListener(_selectedFilesListener);
|
||||
widget.selectedFiles!.addListener(_selectedFilesListener);
|
||||
super.initState();
|
||||
_init();
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ class _LazyLoadingGalleryState extends State<LazyLoadingGallery> {
|
|||
_logger = Logger("LazyLoading_${widget.logTag}");
|
||||
_shouldRender = true;
|
||||
_files = widget.files;
|
||||
_reloadEventSubscription = widget.reloadEvent.listen((e) => _onReload(e));
|
||||
_reloadEventSubscription = widget.reloadEvent!.listen((e) => _onReload(e));
|
||||
|
||||
_currentIndexSubscription =
|
||||
widget.currentIndexStream.listen((currentIndex) {
|
||||
|
@ -96,9 +96,9 @@ class _LazyLoadingGalleryState extends State<LazyLoadingGallery> {
|
|||
|
||||
Future _onReload(FilesUpdatedEvent event) async {
|
||||
final galleryDate =
|
||||
DateTime.fromMicrosecondsSinceEpoch(_files[0].creationTime);
|
||||
DateTime.fromMicrosecondsSinceEpoch(_files![0]!.creationTime!);
|
||||
final filesUpdatedThisDay = event.updatedFiles.where((file) {
|
||||
final fileDate = DateTime.fromMicrosecondsSinceEpoch(file.creationTime);
|
||||
final fileDate = DateTime.fromMicrosecondsSinceEpoch(file.creationTime!);
|
||||
return fileDate.year == galleryDate.year &&
|
||||
fileDate.month == galleryDate.month &&
|
||||
fileDate.day == galleryDate.day;
|
||||
|
@ -125,8 +125,8 @@ class _LazyLoadingGalleryState extends State<LazyLoadingGallery> {
|
|||
}
|
||||
} else if (widget.removalEventTypes.contains(event.type)) {
|
||||
// Files were removed
|
||||
final generatedFileIDs = <int>{};
|
||||
final uploadedFileIds = <int>{};
|
||||
final generatedFileIDs = <int?>{};
|
||||
final uploadedFileIds = <int?>{};
|
||||
for (final file in filesUpdatedThisDay) {
|
||||
if (file.generatedID != null) {
|
||||
generatedFileIDs.add(file.generatedID);
|
||||
|
@ -134,16 +134,16 @@ class _LazyLoadingGalleryState extends State<LazyLoadingGallery> {
|
|||
uploadedFileIds.add(file.uploadedFileID);
|
||||
}
|
||||
}
|
||||
final List<File> files = [];
|
||||
files.addAll(_files);
|
||||
final List<File?> files = [];
|
||||
files.addAll(_files!);
|
||||
files.removeWhere(
|
||||
(file) =>
|
||||
generatedFileIDs.contains(file.generatedID) ||
|
||||
generatedFileIDs.contains(file!.generatedID) ||
|
||||
uploadedFileIds.contains(file.uploadedFileID),
|
||||
);
|
||||
if (kDebugMode) {
|
||||
_logger.finest(
|
||||
"removed ${_files.length - files.length} due to ${event.reason}",
|
||||
"removed ${_files!.length - files.length} due to ${event.reason}",
|
||||
);
|
||||
}
|
||||
if (mounted) {
|
||||
|
@ -163,7 +163,7 @@ class _LazyLoadingGalleryState extends State<LazyLoadingGallery> {
|
|||
void dispose() {
|
||||
_reloadEventSubscription.cancel();
|
||||
_currentIndexSubscription.cancel();
|
||||
widget.selectedFiles.removeListener(_selectedFilesListener);
|
||||
widget.selectedFiles!.removeListener(_selectedFilesListener);
|
||||
_toggleSelectAllFromDay.dispose();
|
||||
_showSelectAllButton.dispose();
|
||||
_areAllFromDaySelected.dispose();
|
||||
|
@ -181,7 +181,7 @@ class _LazyLoadingGalleryState extends State<LazyLoadingGallery> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_files.isEmpty) {
|
||||
if (_files!.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Column(
|
||||
|
@ -191,12 +191,12 @@ class _LazyLoadingGalleryState extends State<LazyLoadingGallery> {
|
|||
children: [
|
||||
getDayWidget(
|
||||
context,
|
||||
_files[0].creationTime,
|
||||
widget.photoGirdSize,
|
||||
_files![0]!.creationTime!,
|
||||
widget.photoGirdSize!,
|
||||
),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _showSelectAllButton,
|
||||
builder: (context, value, _) {
|
||||
builder: (context, dynamic value, _) {
|
||||
return !value
|
||||
? const SizedBox.shrink()
|
||||
: GestureDetector(
|
||||
|
@ -206,7 +206,7 @@ class _LazyLoadingGalleryState extends State<LazyLoadingGallery> {
|
|||
height: 44,
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: _areAllFromDaySelected,
|
||||
builder: (context, value, _) {
|
||||
builder: (context, dynamic value, _) {
|
||||
return value
|
||||
? const Icon(
|
||||
Icons.check_circle,
|
||||
|
@ -232,11 +232,11 @@ class _LazyLoadingGalleryState extends State<LazyLoadingGallery> {
|
|||
)
|
||||
],
|
||||
),
|
||||
_shouldRender
|
||||
_shouldRender!
|
||||
? _getGallery()
|
||||
: PlaceHolderWidget(
|
||||
_files.length,
|
||||
widget.photoGirdSize,
|
||||
_files!.length,
|
||||
widget.photoGirdSize!,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -244,21 +244,21 @@ class _LazyLoadingGalleryState extends State<LazyLoadingGallery> {
|
|||
|
||||
Widget _getGallery() {
|
||||
final List<Widget> childGalleries = [];
|
||||
final subGalleryItemLimit = widget.photoGirdSize < photoGridSizeDefault
|
||||
final subGalleryItemLimit = widget.photoGirdSize! < photoGridSizeDefault
|
||||
? subGalleryLimitMin
|
||||
: subGalleryLimitDefault;
|
||||
for (int index = 0; index < _files.length; index += subGalleryItemLimit) {
|
||||
for (int index = 0; index < _files!.length; index += subGalleryItemLimit) {
|
||||
childGalleries.add(
|
||||
LazyLoadingGridView(
|
||||
widget.tag,
|
||||
_files.sublist(
|
||||
_files!.sublist(
|
||||
index,
|
||||
min(index + subGalleryItemLimit, _files.length),
|
||||
min(index + subGalleryItemLimit, _files!.length),
|
||||
),
|
||||
widget.asyncLoader,
|
||||
widget.selectedFiles,
|
||||
index == 0,
|
||||
_files.length > kRecycleLimit,
|
||||
_files!.length > kRecycleLimit,
|
||||
_toggleSelectAllFromDay,
|
||||
_areAllFromDaySelected,
|
||||
widget.photoGirdSize,
|
||||
|
@ -272,7 +272,7 @@ class _LazyLoadingGalleryState extends State<LazyLoadingGallery> {
|
|||
}
|
||||
|
||||
void _selectedFilesListener() {
|
||||
if (widget.selectedFiles.files.isEmpty) {
|
||||
if (widget.selectedFiles!.files.isEmpty) {
|
||||
_showSelectAllButton.value = false;
|
||||
} else {
|
||||
_showSelectAllButton.value = true;
|
||||
|
@ -282,14 +282,14 @@ class _LazyLoadingGalleryState extends State<LazyLoadingGallery> {
|
|||
|
||||
class LazyLoadingGridView extends StatefulWidget {
|
||||
final String tag;
|
||||
final List<File> filesInDay;
|
||||
final List<File?> filesInDay;
|
||||
final GalleryLoader asyncLoader;
|
||||
final SelectedFiles selectedFiles;
|
||||
final SelectedFiles? selectedFiles;
|
||||
final bool shouldRender;
|
||||
final bool shouldRecycle;
|
||||
final ValueNotifier toggleSelectAllFromDay;
|
||||
final ValueNotifier areAllFilesSelected;
|
||||
final int photoGridSize;
|
||||
final int? photoGridSize;
|
||||
|
||||
LazyLoadingGridView(
|
||||
this.tag,
|
||||
|
@ -301,7 +301,7 @@ class LazyLoadingGridView extends StatefulWidget {
|
|||
this.toggleSelectAllFromDay,
|
||||
this.areAllFilesSelected,
|
||||
this.photoGridSize, {
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key ?? UniqueKey());
|
||||
|
||||
@override
|
||||
|
@ -309,15 +309,15 @@ class LazyLoadingGridView extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _LazyLoadingGridViewState extends State<LazyLoadingGridView> {
|
||||
bool _shouldRender;
|
||||
int _currentUserID;
|
||||
StreamSubscription<ClearSelectionsEvent> _clearSelectionsEvent;
|
||||
bool? _shouldRender;
|
||||
int? _currentUserID;
|
||||
late StreamSubscription<ClearSelectionsEvent> _clearSelectionsEvent;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_shouldRender = widget.shouldRender;
|
||||
_currentUserID = Configuration.instance.getUserID();
|
||||
widget.selectedFiles.addListener(_selectedFilesListener);
|
||||
widget.selectedFiles!.addListener(_selectedFilesListener);
|
||||
_clearSelectionsEvent =
|
||||
Bus.instance.on<ClearSelectionsEvent>().listen((event) {
|
||||
if (mounted) {
|
||||
|
@ -330,7 +330,7 @@ class _LazyLoadingGridViewState extends State<LazyLoadingGridView> {
|
|||
|
||||
@override
|
||||
void dispose() {
|
||||
widget.selectedFiles.removeListener(_selectedFilesListener);
|
||||
widget.selectedFiles!.removeListener(_selectedFilesListener);
|
||||
_clearSelectionsEvent.cancel();
|
||||
widget.toggleSelectAllFromDay
|
||||
.removeListener(_toggleSelectAllFromDayListener);
|
||||
|
@ -365,25 +365,25 @@ class _LazyLoadingGridViewState extends State<LazyLoadingGridView> {
|
|||
});
|
||||
}
|
||||
},
|
||||
child: _shouldRender
|
||||
child: _shouldRender!
|
||||
? _getGridView()
|
||||
: PlaceHolderWidget(widget.filesInDay.length, widget.photoGridSize),
|
||||
: PlaceHolderWidget(widget.filesInDay.length, widget.photoGridSize!),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _getNonRecyclableView() {
|
||||
if (!_shouldRender) {
|
||||
if (!_shouldRender!) {
|
||||
return VisibilityDetector(
|
||||
key: UniqueKey(),
|
||||
onVisibilityChanged: (visibility) {
|
||||
if (mounted && visibility.visibleFraction > 0 && !_shouldRender) {
|
||||
if (mounted && visibility.visibleFraction > 0 && !_shouldRender!) {
|
||||
setState(() {
|
||||
_shouldRender = true;
|
||||
});
|
||||
}
|
||||
},
|
||||
child:
|
||||
PlaceHolderWidget(widget.filesInDay.length, widget.photoGridSize),
|
||||
PlaceHolderWidget(widget.filesInDay.length, widget.photoGridSize!),
|
||||
);
|
||||
} else {
|
||||
return _getGridView();
|
||||
|
@ -396,34 +396,34 @@ class _LazyLoadingGridViewState extends State<LazyLoadingGridView> {
|
|||
physics: const NeverScrollableScrollPhysics(),
|
||||
// to disable GridView's scrolling
|
||||
itemBuilder: (context, index) {
|
||||
return _buildFile(context, widget.filesInDay[index]);
|
||||
return _buildFile(context, widget.filesInDay[index]!);
|
||||
},
|
||||
itemCount: widget.filesInDay.length,
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisSpacing: 2,
|
||||
mainAxisSpacing: 2,
|
||||
crossAxisCount: widget.photoGridSize,
|
||||
crossAxisCount: widget.photoGridSize!,
|
||||
),
|
||||
padding: const EdgeInsets.all(0),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFile(BuildContext context, File file) {
|
||||
final isFileSelected = widget.selectedFiles.isFileSelected(file);
|
||||
final isFileSelected = widget.selectedFiles!.isFileSelected(file);
|
||||
Color selectionColor = Colors.white;
|
||||
if (isFileSelected &&
|
||||
file.isUploaded &&
|
||||
(file.ownerID != _currentUserID ||
|
||||
file.pubMagicMetadata.uploaderName != null)) {
|
||||
file.pubMagicMetadata!.uploaderName != null)) {
|
||||
final avatarColors = getEnteColorScheme(context).avatarColors;
|
||||
final int randomID = file.ownerID != _currentUserID
|
||||
? file.ownerID
|
||||
: file.pubMagicMetadata.uploaderName.sumAsciiValues;
|
||||
? file.ownerID!
|
||||
: file.pubMagicMetadata!.uploaderName.sumAsciiValues;
|
||||
selectionColor = avatarColors[(randomID).remainder(avatarColors.length)];
|
||||
}
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
if (widget.selectedFiles.files.isNotEmpty) {
|
||||
if (widget.selectedFiles!.files.isNotEmpty) {
|
||||
_selectFile(file);
|
||||
} else {
|
||||
_routeToDetailPage(file, context);
|
||||
|
@ -452,7 +452,7 @@ class _LazyLoadingGridViewState extends State<LazyLoadingGridView> {
|
|||
serverLoadDeferDuration: thumbnailServerLoadDeferDuration,
|
||||
shouldShowLivePhotoOverlay: true,
|
||||
key: Key(widget.tag + file.tag),
|
||||
thumbnailSize: widget.photoGridSize < photoGridSizeDefault
|
||||
thumbnailSize: widget.photoGridSize! < photoGridSizeDefault
|
||||
? thumbnailLargeSize
|
||||
: thumbnailSmallSize,
|
||||
shouldShowOwnerAvatar: !isFileSelected,
|
||||
|
@ -478,7 +478,7 @@ class _LazyLoadingGridViewState extends State<LazyLoadingGridView> {
|
|||
}
|
||||
|
||||
void _selectFile(File file) {
|
||||
widget.selectedFiles.toggleSelection(file);
|
||||
widget.selectedFiles!.toggleSelection(file);
|
||||
}
|
||||
|
||||
void _routeToDetailPage(File file, BuildContext context) {
|
||||
|
@ -494,14 +494,14 @@ class _LazyLoadingGridViewState extends State<LazyLoadingGridView> {
|
|||
}
|
||||
|
||||
void _selectedFilesListener() {
|
||||
if (widget.selectedFiles.files.containsAll(widget.filesInDay.toSet())) {
|
||||
if (widget.selectedFiles!.files.containsAll(widget.filesInDay.toSet())) {
|
||||
widget.areAllFilesSelected.value = true;
|
||||
} else {
|
||||
widget.areAllFilesSelected.value = false;
|
||||
}
|
||||
bool shouldRefresh = false;
|
||||
for (final file in widget.filesInDay) {
|
||||
if (widget.selectedFiles.isPartOfLastSelected(file)) {
|
||||
if (widget.selectedFiles!.isPartOfLastSelected(file!)) {
|
||||
shouldRefresh = true;
|
||||
}
|
||||
}
|
||||
|
@ -511,12 +511,12 @@ class _LazyLoadingGridViewState extends State<LazyLoadingGridView> {
|
|||
}
|
||||
|
||||
void _toggleSelectAllFromDayListener() {
|
||||
if (widget.selectedFiles.files.containsAll(widget.filesInDay.toSet())) {
|
||||
if (widget.selectedFiles!.files.containsAll(widget.filesInDay.toSet())) {
|
||||
setState(() {
|
||||
widget.selectedFiles.unSelectAll(widget.filesInDay.toSet());
|
||||
widget.selectedFiles!.unSelectAll(widget.filesInDay.toSet() as Set<File>);
|
||||
});
|
||||
} else {
|
||||
widget.selectedFiles.selectAll(widget.filesInDay.toSet());
|
||||
widget.selectedFiles!.selectAll(widget.filesInDay.toSet() as Set<File>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
@ -7,8 +7,8 @@ class ScrollBarThumb extends StatelessWidget {
|
|||
final Color drawColor;
|
||||
final double height;
|
||||
final String title;
|
||||
final Animation labelAnimation;
|
||||
final Animation thumbAnimation;
|
||||
final Animation? labelAnimation;
|
||||
final Animation? thumbAnimation;
|
||||
final Function(DragStartDetails details) onDragStart;
|
||||
final Function(DragUpdateDetails details) onDragUpdate;
|
||||
final Function(DragEndDetails details) onDragEnd;
|
||||
|
@ -23,7 +23,7 @@ class ScrollBarThumb extends StatelessWidget {
|
|||
this.onDragStart,
|
||||
this.onDragUpdate,
|
||||
this.onDragEnd, {
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -33,7 +33,7 @@ class ScrollBarThumb extends StatelessWidget {
|
|||
children: [
|
||||
IgnorePointer(
|
||||
child: FadeTransition(
|
||||
opacity: labelAnimation,
|
||||
opacity: labelAnimation as Animation<double>,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.fromLTRB(20, 12, 20, 12),
|
||||
decoration: BoxDecoration(
|
||||
|
@ -60,7 +60,7 @@ class ScrollBarThumb extends StatelessWidget {
|
|||
onVerticalDragUpdate: onDragUpdate,
|
||||
onVerticalDragEnd: onDragEnd,
|
||||
child: SlideFadeTransition(
|
||||
animation: thumbAnimation,
|
||||
animation: thumbAnimation as Animation<double>?,
|
||||
child: CustomPaint(
|
||||
foregroundPainter: _ArrowCustomPainter(drawColor),
|
||||
child: Material(
|
||||
|
@ -131,27 +131,27 @@ class _ArrowCustomPainter extends CustomPainter {
|
|||
}
|
||||
|
||||
class SlideFadeTransition extends StatelessWidget {
|
||||
final Animation<double> animation;
|
||||
final Animation<double>? animation;
|
||||
final Widget child;
|
||||
|
||||
const SlideFadeTransition({
|
||||
Key key,
|
||||
@required this.animation,
|
||||
@required this.child,
|
||||
Key? key,
|
||||
required this.animation,
|
||||
required this.child,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedBuilder(
|
||||
animation: animation,
|
||||
builder: (context, child) => animation.value == 0.0 ? Container() : child,
|
||||
animation: animation!,
|
||||
builder: (context, child) => animation!.value == 0.0 ? Container() : child!,
|
||||
child: SlideTransition(
|
||||
position: Tween(
|
||||
begin: const Offset(0.3, 0.0),
|
||||
end: const Offset(0.0, 0.0),
|
||||
).animate(animation),
|
||||
).animate(animation!),
|
||||
child: FadeTransition(
|
||||
opacity: animation,
|
||||
opacity: animation!,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class LifecycleEventHandler extends WidgetsBindingObserver {
|
||||
final AsyncCallback resumeCallBack;
|
||||
final AsyncCallback suspendingCallBack;
|
||||
final AsyncCallback? resumeCallBack;
|
||||
final AsyncCallback? suspendingCallBack;
|
||||
|
||||
LifecycleEventHandler({
|
||||
this.resumeCallBack,
|
||||
|
@ -17,14 +17,14 @@ class LifecycleEventHandler extends WidgetsBindingObserver {
|
|||
switch (state) {
|
||||
case AppLifecycleState.resumed:
|
||||
if (resumeCallBack != null) {
|
||||
await resumeCallBack();
|
||||
await resumeCallBack!();
|
||||
}
|
||||
break;
|
||||
case AppLifecycleState.inactive:
|
||||
case AppLifecycleState.paused:
|
||||
case AppLifecycleState.detached:
|
||||
if (suspendingCallBack != null) {
|
||||
await suspendingCallBack();
|
||||
await suspendingCallBack!();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
@ -15,15 +15,15 @@ import 'package:photos/ui/common/bottom_shadow.dart';
|
|||
import 'package:photos/utils/navigation_util.dart';
|
||||
|
||||
class LoadingPhotosWidget extends StatefulWidget {
|
||||
const LoadingPhotosWidget({Key key}) : super(key: key);
|
||||
const LoadingPhotosWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<LoadingPhotosWidget> createState() => _LoadingPhotosWidgetState();
|
||||
}
|
||||
|
||||
class _LoadingPhotosWidgetState extends State<LoadingPhotosWidget> {
|
||||
StreamSubscription<SyncStatusUpdate> _firstImportEvent;
|
||||
StreamSubscription<LocalImportProgressEvent> _imprortProgressEvent;
|
||||
late StreamSubscription<SyncStatusUpdate> _firstImportEvent;
|
||||
late StreamSubscription<LocalImportProgressEvent> _imprortProgressEvent;
|
||||
int _currentPage = 0;
|
||||
String _loadingMessage = "Loading your photos...";
|
||||
final PageController _pageController = PageController(
|
||||
|
@ -145,7 +145,7 @@ class _LoadingPhotosWidgetState extends State<LoadingPhotosWidget> {
|
|||
children: [
|
||||
Text(
|
||||
"Did you know?",
|
||||
style: Theme.of(context).textTheme.headline6.copyWith(
|
||||
style: Theme.of(context).textTheme.headline6!.copyWith(
|
||||
color: Theme.of(context).colorScheme.greenText,
|
||||
),
|
||||
),
|
||||
|
@ -192,7 +192,7 @@ class _LoadingPhotosWidgetState extends State<LoadingPhotosWidget> {
|
|||
textAlign: TextAlign.start,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.headline5
|
||||
.headline5!
|
||||
.copyWith(color: Theme.of(context).colorScheme.defaultTextColor),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
library google_nav_bar;
|
||||
|
||||
|
@ -7,7 +7,7 @@ import 'package:flutter/services.dart';
|
|||
|
||||
class GNav extends StatefulWidget {
|
||||
const GNav({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.tabs,
|
||||
this.selectedIndex = 0,
|
||||
this.onTabChange,
|
||||
|
@ -34,29 +34,29 @@ class GNav extends StatefulWidget {
|
|||
this.mainAxisAlignment = MainAxisAlignment.spaceBetween,
|
||||
}) : super(key: key);
|
||||
|
||||
final List<GButton> tabs;
|
||||
final List<GButton>? tabs;
|
||||
final int selectedIndex;
|
||||
final Function onTabChange;
|
||||
final double gap;
|
||||
final double tabBorderRadius;
|
||||
final double iconSize;
|
||||
final Color activeColor;
|
||||
final Color backgroundColor;
|
||||
final Color tabBackgroundColor;
|
||||
final Color color;
|
||||
final Color rippleColor;
|
||||
final Color hoverColor;
|
||||
final EdgeInsetsGeometry padding;
|
||||
final EdgeInsetsGeometry tabMargin;
|
||||
final TextStyle textStyle;
|
||||
final Duration duration;
|
||||
final Curve curve;
|
||||
final bool debug;
|
||||
final bool haptic;
|
||||
final Border tabBorder;
|
||||
final Border tabActiveBorder;
|
||||
final List<BoxShadow> tabShadow;
|
||||
final Gradient tabBackgroundGradient;
|
||||
final Function? onTabChange;
|
||||
final double? gap;
|
||||
final double? tabBorderRadius;
|
||||
final double? iconSize;
|
||||
final Color? activeColor;
|
||||
final Color? backgroundColor;
|
||||
final Color? tabBackgroundColor;
|
||||
final Color? color;
|
||||
final Color? rippleColor;
|
||||
final Color? hoverColor;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
final EdgeInsetsGeometry? tabMargin;
|
||||
final TextStyle? textStyle;
|
||||
final Duration? duration;
|
||||
final Curve? curve;
|
||||
final bool? debug;
|
||||
final bool? haptic;
|
||||
final Border? tabBorder;
|
||||
final Border? tabActiveBorder;
|
||||
final List<BoxShadow>? tabShadow;
|
||||
final Gradient? tabBackgroundGradient;
|
||||
final MainAxisAlignment mainAxisAlignment;
|
||||
|
||||
@override
|
||||
|
@ -64,7 +64,7 @@ class GNav extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _GNavState extends State<GNav> {
|
||||
int selectedIndex;
|
||||
int? selectedIndex;
|
||||
bool clickable = true;
|
||||
|
||||
@override
|
||||
|
@ -83,20 +83,20 @@ class _GNavState extends State<GNav> {
|
|||
color: widget.backgroundColor ?? Colors.transparent,
|
||||
child: Row(
|
||||
mainAxisAlignment: widget.mainAxisAlignment,
|
||||
children: widget.tabs
|
||||
children: widget.tabs!
|
||||
.map(
|
||||
(t) => GButton(
|
||||
key: t.key,
|
||||
border: t.border ?? widget.tabBorder,
|
||||
activeBorder: t.activeBorder ?? widget.tabActiveBorder,
|
||||
borderRadius: t.borderRadius ?? widget.tabBorderRadius != null
|
||||
borderRadius: t.borderRadius as bool? ?? widget.tabBorderRadius != null
|
||||
? BorderRadius.all(
|
||||
Radius.circular(widget.tabBorderRadius),
|
||||
Radius.circular(widget.tabBorderRadius!),
|
||||
)
|
||||
: const BorderRadius.all(Radius.circular(100.0)),
|
||||
debug: widget.debug ?? false,
|
||||
margin: t.margin ?? widget.tabMargin,
|
||||
active: selectedIndex == widget.tabs.indexOf(t),
|
||||
active: selectedIndex == widget.tabs!.indexOf(t),
|
||||
gap: t.gap ?? widget.gap,
|
||||
iconActiveColor: t.iconActiveColor ?? widget.activeColor,
|
||||
iconColor: t.iconColor ?? widget.color,
|
||||
|
@ -118,7 +118,7 @@ class _GNavState extends State<GNav> {
|
|||
Colors.transparent,
|
||||
duration: widget.duration ?? const Duration(milliseconds: 500),
|
||||
onPressed: () {
|
||||
widget.onTabChange(widget.tabs.indexOf(t));
|
||||
widget.onTabChange!(widget.tabs!.indexOf(t));
|
||||
},
|
||||
),
|
||||
)
|
||||
|
@ -129,35 +129,35 @@ class _GNavState extends State<GNav> {
|
|||
}
|
||||
|
||||
class GButton extends StatefulWidget {
|
||||
final bool active;
|
||||
final bool debug;
|
||||
final bool haptic;
|
||||
final double gap;
|
||||
final Color iconColor;
|
||||
final Color rippleColor;
|
||||
final Color hoverColor;
|
||||
final Color iconActiveColor;
|
||||
final Color textColor;
|
||||
final EdgeInsetsGeometry padding;
|
||||
final EdgeInsetsGeometry margin;
|
||||
final TextStyle textStyle;
|
||||
final double iconSize;
|
||||
final Function onPressed;
|
||||
final bool? active;
|
||||
final bool? debug;
|
||||
final bool? haptic;
|
||||
final double? gap;
|
||||
final Color? iconColor;
|
||||
final Color? rippleColor;
|
||||
final Color? hoverColor;
|
||||
final Color? iconActiveColor;
|
||||
final Color? textColor;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
final EdgeInsetsGeometry? margin;
|
||||
final TextStyle? textStyle;
|
||||
final double? iconSize;
|
||||
final Function? onPressed;
|
||||
final String text;
|
||||
final IconData icon;
|
||||
final Color backgroundColor;
|
||||
final Duration duration;
|
||||
final Curve curve;
|
||||
final Gradient backgroundGradient;
|
||||
final Widget leading;
|
||||
final BorderRadius borderRadius;
|
||||
final Border border;
|
||||
final Border activeBorder;
|
||||
final List<BoxShadow> shadow;
|
||||
final String semanticLabel;
|
||||
final IconData? icon;
|
||||
final Color? backgroundColor;
|
||||
final Duration? duration;
|
||||
final Curve? curve;
|
||||
final Gradient? backgroundGradient;
|
||||
final Widget? leading;
|
||||
final BorderRadius? borderRadius;
|
||||
final Border? border;
|
||||
final Border? activeBorder;
|
||||
final List<BoxShadow>? shadow;
|
||||
final String? semanticLabel;
|
||||
|
||||
const GButton({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.active,
|
||||
this.haptic,
|
||||
this.backgroundColor,
|
||||
|
@ -205,8 +205,8 @@ class _GButtonState extends State<GButton> {
|
|||
iconSize: widget.iconSize,
|
||||
active: widget.active,
|
||||
onPressed: () {
|
||||
if (widget.haptic) HapticFeedback.selectionClick();
|
||||
widget.onPressed();
|
||||
if (widget.haptic!) HapticFeedback.selectionClick();
|
||||
widget.onPressed!();
|
||||
},
|
||||
padding: widget.padding,
|
||||
margin: widget.margin,
|
||||
|
@ -227,7 +227,7 @@ class _GButtonState extends State<GButton> {
|
|||
|
||||
class Button extends StatefulWidget {
|
||||
const Button({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.icon,
|
||||
this.iconSize,
|
||||
this.leading,
|
||||
|
@ -252,38 +252,38 @@ class Button extends StatefulWidget {
|
|||
this.shadow,
|
||||
}) : super(key: key);
|
||||
|
||||
final IconData icon;
|
||||
final double iconSize;
|
||||
final Text text;
|
||||
final Widget leading;
|
||||
final Color iconActiveColor;
|
||||
final Color iconColor;
|
||||
final Color color;
|
||||
final Color rippleColor;
|
||||
final Color hoverColor;
|
||||
final double gap;
|
||||
final bool active;
|
||||
final bool debug;
|
||||
final VoidCallback onPressed;
|
||||
final EdgeInsetsGeometry padding;
|
||||
final EdgeInsetsGeometry margin;
|
||||
final Duration duration;
|
||||
final Curve curve;
|
||||
final Gradient gradient;
|
||||
final BorderRadius borderRadius;
|
||||
final Border border;
|
||||
final Border activeBorder;
|
||||
final List<BoxShadow> shadow;
|
||||
final IconData? icon;
|
||||
final double? iconSize;
|
||||
final Text? text;
|
||||
final Widget? leading;
|
||||
final Color? iconActiveColor;
|
||||
final Color? iconColor;
|
||||
final Color? color;
|
||||
final Color? rippleColor;
|
||||
final Color? hoverColor;
|
||||
final double? gap;
|
||||
final bool? active;
|
||||
final bool? debug;
|
||||
final VoidCallback? onPressed;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
final EdgeInsetsGeometry? margin;
|
||||
final Duration? duration;
|
||||
final Curve? curve;
|
||||
final Gradient? gradient;
|
||||
final BorderRadius? borderRadius;
|
||||
final Border? border;
|
||||
final Border? activeBorder;
|
||||
final List<BoxShadow>? shadow;
|
||||
|
||||
@override
|
||||
State<Button> createState() => _ButtonState();
|
||||
}
|
||||
|
||||
class _ButtonState extends State<Button> with TickerProviderStateMixin {
|
||||
bool _expanded;
|
||||
bool? _expanded;
|
||||
|
||||
AnimationController expandController;
|
||||
Animation<double> animation;
|
||||
late AnimationController expandController;
|
||||
Animation<double>? animation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -302,8 +302,8 @@ class _ButtonState extends State<Button> with TickerProviderStateMixin {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_expanded = !widget.active;
|
||||
if (_expanded) {
|
||||
_expanded = !widget.active!;
|
||||
if (_expanded!) {
|
||||
expandController.reverse();
|
||||
} else {
|
||||
expandController.forward();
|
||||
|
@ -312,7 +312,7 @@ class _ButtonState extends State<Button> with TickerProviderStateMixin {
|
|||
final Widget icon = widget.leading ??
|
||||
Icon(
|
||||
widget.icon,
|
||||
color: _expanded ? widget.iconColor : widget.iconActiveColor,
|
||||
color: _expanded! ? widget.iconColor : widget.iconActiveColor,
|
||||
size: widget.iconSize,
|
||||
);
|
||||
|
||||
|
@ -323,23 +323,23 @@ class _ButtonState extends State<Button> with TickerProviderStateMixin {
|
|||
splashColor: widget.rippleColor,
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
onTap: () {
|
||||
widget.onPressed();
|
||||
widget.onPressed!();
|
||||
},
|
||||
child: Container(
|
||||
padding: widget.margin,
|
||||
child: AnimatedContainer(
|
||||
curve: Curves.easeOut,
|
||||
padding: widget.padding,
|
||||
duration: widget.duration,
|
||||
duration: widget.duration!,
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: widget.shadow,
|
||||
border: widget.active
|
||||
border: widget.active!
|
||||
? (widget.activeBorder ?? widget.border)
|
||||
: widget.border,
|
||||
gradient: widget.gradient,
|
||||
color: _expanded
|
||||
? widget.color.withOpacity(0)
|
||||
: widget.debug
|
||||
color: _expanded!
|
||||
? widget.color!.withOpacity(0)
|
||||
: widget.debug!
|
||||
? Colors.red
|
||||
: widget.gradient != null
|
||||
? Colors.white
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// @dart=2.9
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:expansion_tile_card/expansion_tile_card.dart';
|
||||
|
@ -10,7 +8,7 @@ import 'package:photos/ui/common/loading_widget.dart';
|
|||
|
||||
class BillingQuestionsWidget extends StatelessWidget {
|
||||
const BillingQuestionsWidget({
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -64,11 +62,11 @@ class BillingQuestionsWidget extends StatelessWidget {
|
|||
|
||||
class FaqWidget extends StatelessWidget {
|
||||
const FaqWidget({
|
||||
Key key,
|
||||
@required this.faq,
|
||||
Key? key,
|
||||
required this.faq,
|
||||
}) : super(key: key);
|
||||
|
||||
final FaqItem faq;
|
||||
final FaqItem? faq;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -76,7 +74,7 @@ class FaqWidget extends StatelessWidget {
|
|||
padding: const EdgeInsets.all(2),
|
||||
child: ExpansionTileCard(
|
||||
elevation: 0,
|
||||
title: Text(faq.q),
|
||||
title: Text(faq!.q!),
|
||||
expandedTextColor: Theme.of(context).colorScheme.greenAlternative,
|
||||
baseColor: Theme.of(context).cardColor,
|
||||
children: [
|
||||
|
@ -87,7 +85,7 @@ class FaqWidget extends StatelessWidget {
|
|||
bottom: 12,
|
||||
),
|
||||
child: Text(
|
||||
faq.a,
|
||||
faq!.a!,
|
||||
style: const TextStyle(
|
||||
height: 1.5,
|
||||
),
|
||||
|
@ -100,16 +98,16 @@ class FaqWidget extends StatelessWidget {
|
|||
}
|
||||
|
||||
class FaqItem {
|
||||
final String q;
|
||||
final String a;
|
||||
final String? q;
|
||||
final String? a;
|
||||
FaqItem({
|
||||
this.q,
|
||||
this.a,
|
||||
});
|
||||
|
||||
FaqItem copyWith({
|
||||
String q,
|
||||
String a,
|
||||
String? q,
|
||||
String? a,
|
||||
}) {
|
||||
return FaqItem(
|
||||
q: q ?? this.q,
|
||||
|
@ -125,11 +123,9 @@ class FaqItem {
|
|||
}
|
||||
|
||||
factory FaqItem.fromMap(Map<String, dynamic> map) {
|
||||
if (map == null) return null;
|
||||
|
||||
return FaqItem(
|
||||
q: map['q'],
|
||||
a: map['a'],
|
||||
q: map['q'] ?? 'q',
|
||||
a: map['a'] ?? 'a',
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// @dart=2.9
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:collection/collection.dart' show IterableNullableExtension;
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
|
@ -15,10 +14,10 @@ import 'package:photos/ui/common/progress_dialog.dart';
|
|||
import 'package:photos/utils/dialog_util.dart';
|
||||
|
||||
class PaymentWebPage extends StatefulWidget {
|
||||
final String planId;
|
||||
final String actionType;
|
||||
final String? planId;
|
||||
final String? actionType;
|
||||
|
||||
const PaymentWebPage({Key key, this.planId, this.actionType})
|
||||
const PaymentWebPage({Key? key, this.planId, this.actionType})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -30,10 +29,10 @@ class _PaymentWebPageState extends State<PaymentWebPage> {
|
|||
final UserService userService = UserService.instance;
|
||||
final BillingService billingService = BillingService.instance;
|
||||
final String basePaymentUrl = kWebPaymentBaseEndpoint;
|
||||
ProgressDialog _dialog;
|
||||
InAppWebViewController webView;
|
||||
late ProgressDialog _dialog;
|
||||
InAppWebViewController? webView;
|
||||
double progress = 0;
|
||||
Uri initPaymentUrl;
|
||||
Uri? initPaymentUrl;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -54,7 +53,7 @@ class _PaymentWebPageState extends State<PaymentWebPage> {
|
|||
return const EnteLoadingWidget();
|
||||
}
|
||||
return WillPopScope(
|
||||
onWillPop: () async => _buildPageExitWidget(context),
|
||||
onWillPop: (() async => _buildPageExitWidget(context)),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Subscription'),
|
||||
|
@ -83,7 +82,7 @@ class _PaymentWebPageState extends State<PaymentWebPage> {
|
|||
_logger.info("Loading url $loadingUri");
|
||||
// handle the payment response
|
||||
if (_isPaymentActionComplete(loadingUri)) {
|
||||
await _handlePaymentResponse(loadingUri);
|
||||
await _handlePaymentResponse(loadingUri!);
|
||||
return NavigationActionPolicy.CANCEL;
|
||||
}
|
||||
return NavigationActionPolicy.ALLOW;
|
||||
|
@ -113,7 +112,7 @@ class _PaymentWebPageState extends State<PaymentWebPage> {
|
|||
},
|
||||
),
|
||||
),
|
||||
].where((Object o) => o != null).toList(),
|
||||
].whereNotNull().toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -125,7 +124,7 @@ class _PaymentWebPageState extends State<PaymentWebPage> {
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
Uri _getPaymentUrl(String paymentToken) {
|
||||
Uri _getPaymentUrl(String? paymentToken) {
|
||||
final queryParameters = {
|
||||
'productID': widget.planId,
|
||||
'paymentToken': paymentToken,
|
||||
|
@ -134,15 +133,15 @@ class _PaymentWebPageState extends State<PaymentWebPage> {
|
|||
};
|
||||
final tryParse = Uri.tryParse(kWebPaymentBaseEndpoint);
|
||||
if (kDebugMode && kWebPaymentBaseEndpoint.startsWith("http://")) {
|
||||
return Uri.http(tryParse.authority, tryParse.path, queryParameters);
|
||||
return Uri.http(tryParse!.authority, tryParse.path, queryParameters);
|
||||
} else {
|
||||
return Uri.https(tryParse.authority, tryParse.path, queryParameters);
|
||||
return Uri.https(tryParse!.authority, tryParse.path, queryParameters);
|
||||
}
|
||||
}
|
||||
|
||||
// show dialog to handle accidental back press.
|
||||
Future<bool> _buildPageExitWidget(BuildContext context) {
|
||||
return showDialog(
|
||||
Future<bool> _buildPageExitWidget(BuildContext context) async {
|
||||
final result = await showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Are you sure you want to exit?'),
|
||||
|
@ -168,9 +167,13 @@ class _PaymentWebPageState extends State<PaymentWebPage> {
|
|||
],
|
||||
),
|
||||
);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _isPaymentActionComplete(Uri loadingUri) {
|
||||
bool _isPaymentActionComplete(Uri? loadingUri) {
|
||||
return loadingUri.toString().startsWith(kWebPaymentRedirectUrl);
|
||||
}
|
||||
|
||||
|
@ -240,13 +243,13 @@ class _PaymentWebPageState extends State<PaymentWebPage> {
|
|||
}
|
||||
|
||||
// warn the user to wait for sometime before trying another payment
|
||||
Future<dynamic> _showExitPageDialog({String title, String content}) {
|
||||
Future<dynamic> _showExitPageDialog({String? title, String? content}) {
|
||||
return showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(title),
|
||||
content: Text(content),
|
||||
title: Text(title!),
|
||||
content: Text(content!),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: Text(
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
|
@ -30,7 +30,7 @@ class StripeSubscriptionPage extends StatefulWidget {
|
|||
|
||||
const StripeSubscriptionPage({
|
||||
this.isOnboarding = false,
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -40,13 +40,13 @@ class StripeSubscriptionPage extends StatefulWidget {
|
|||
class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
|
||||
final _billingService = BillingService.instance;
|
||||
final _userService = UserService.instance;
|
||||
Subscription _currentSubscription;
|
||||
ProgressDialog _dialog;
|
||||
UserDetails _userDetails;
|
||||
Subscription? _currentSubscription;
|
||||
late ProgressDialog _dialog;
|
||||
late UserDetails _userDetails;
|
||||
|
||||
// indicates if user's subscription plan is still active
|
||||
bool _hasActiveSubscription;
|
||||
FreePlan _freePlan;
|
||||
late bool _hasActiveSubscription;
|
||||
late FreePlan _freePlan;
|
||||
List<BillingPlan> _plans = [];
|
||||
bool _hasLoadedData = false;
|
||||
bool _isLoading = false;
|
||||
|
@ -64,9 +64,9 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
|
|||
.then((userDetails) async {
|
||||
_userDetails = userDetails;
|
||||
_currentSubscription = userDetails.subscription;
|
||||
_showYearlyPlan = _currentSubscription.isYearlyPlan();
|
||||
_hasActiveSubscription = _currentSubscription.isValid();
|
||||
_isStripeSubscriber = _currentSubscription.paymentProvider == stripe;
|
||||
_showYearlyPlan = _currentSubscription!.isYearlyPlan();
|
||||
_hasActiveSubscription = _currentSubscription!.isValid();
|
||||
_isStripeSubscriber = _currentSubscription!.paymentProvider == stripe;
|
||||
return _filterStripeForUI().then((value) {
|
||||
_hasLoadedData = true;
|
||||
setState(() {});
|
||||
|
@ -101,8 +101,8 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
|
|||
// verify user has subscribed before redirecting to main page
|
||||
if (widget.isOnboarding &&
|
||||
_currentSubscription != null &&
|
||||
_currentSubscription.isValid() &&
|
||||
_currentSubscription.productID != freeProductID) {
|
||||
_currentSubscription!.isValid() &&
|
||||
_currentSubscription!.productID != freeProductID) {
|
||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||
}
|
||||
}
|
||||
|
@ -203,7 +203,7 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
|
|||
widgets.add(ValidityWidget(currentSubscription: _currentSubscription));
|
||||
}
|
||||
|
||||
if (_currentSubscription.productID == freeProductID) {
|
||||
if (_currentSubscription!.productID == freeProductID) {
|
||||
if (widget.isOnboarding) {
|
||||
widgets.add(SkipSubscriptionWidget(freePlan: _freePlan));
|
||||
}
|
||||
|
@ -215,22 +215,22 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
|
|||
widgets.add(_stripeRenewOrCancelButton());
|
||||
}
|
||||
|
||||
if (_currentSubscription.productID != freeProductID) {
|
||||
if (_currentSubscription!.productID != freeProductID) {
|
||||
widgets.addAll([
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
final String paymentProvider =
|
||||
_currentSubscription.paymentProvider;
|
||||
switch (_currentSubscription.paymentProvider) {
|
||||
_currentSubscription!.paymentProvider;
|
||||
switch (_currentSubscription!.paymentProvider) {
|
||||
case stripe:
|
||||
await _launchStripePortal();
|
||||
break;
|
||||
case playStore:
|
||||
launchUrlString(
|
||||
"https://play.google.com/store/account/subscriptions?sku=" +
|
||||
_currentSubscription.productID +
|
||||
_currentSubscription!.productID +
|
||||
"&package=io.ente.photos",
|
||||
);
|
||||
break;
|
||||
|
@ -288,7 +288,7 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
|
|||
RichText(
|
||||
text: TextSpan(
|
||||
text: "Manage family",
|
||||
style: Theme.of(context).textTheme.bodyMedium.copyWith(
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
|
@ -330,7 +330,7 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
|
|||
|
||||
Widget _stripeRenewOrCancelButton() {
|
||||
final bool isRenewCancelled =
|
||||
_currentSubscription.attributes?.isCancelled ?? false;
|
||||
_currentSubscription!.attributes?.isCancelled ?? false;
|
||||
final String title =
|
||||
isRenewCancelled ? "Renew subscription" : "Cancel subscription";
|
||||
return TextButton(
|
||||
|
@ -397,7 +397,7 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
|
|||
continue;
|
||||
}
|
||||
final isActive =
|
||||
_hasActiveSubscription && _currentSubscription.productID == productID;
|
||||
_hasActiveSubscription && _currentSubscription!.productID == productID;
|
||||
if (isActive) {
|
||||
foundActivePlan = true;
|
||||
}
|
||||
|
@ -412,12 +412,12 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
|
|||
// payment providers
|
||||
if (!_isStripeSubscriber &&
|
||||
_hasActiveSubscription &&
|
||||
_currentSubscription.productID != freeProductID) {
|
||||
_currentSubscription!.productID != freeProductID) {
|
||||
showErrorDialog(
|
||||
context,
|
||||
"Sorry",
|
||||
"Please cancel your existing subscription from "
|
||||
"${_currentSubscription.paymentProvider} first",
|
||||
"${_currentSubscription!.paymentProvider} first",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -515,13 +515,13 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
|
|||
void _addCurrentPlanWidget(List<Widget> planWidgets) {
|
||||
// don't add current plan if it's monthly plan but UI is showing yearly plans
|
||||
// and vice versa.
|
||||
if (_showYearlyPlan != _currentSubscription.isYearlyPlan() &&
|
||||
_currentSubscription.productID != freeProductID) {
|
||||
if (_showYearlyPlan != _currentSubscription!.isYearlyPlan() &&
|
||||
_currentSubscription!.productID != freeProductID) {
|
||||
return;
|
||||
}
|
||||
int activePlanIndex = 0;
|
||||
for (; activePlanIndex < _plans.length; activePlanIndex++) {
|
||||
if (_plans[activePlanIndex].storage > _currentSubscription.storage) {
|
||||
if (_plans[activePlanIndex].storage > _currentSubscription!.storage) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -531,9 +531,9 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
|
|||
child: InkWell(
|
||||
onTap: () {},
|
||||
child: SubscriptionPlanWidget(
|
||||
storage: _currentSubscription.storage,
|
||||
price: _currentSubscription.price,
|
||||
period: _currentSubscription.period,
|
||||
storage: _currentSubscription!.storage,
|
||||
price: _currentSubscription!.price,
|
||||
period: _currentSubscription!.period,
|
||||
isActive: true,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/ente_theme_data.dart';
|
||||
|
@ -8,11 +8,11 @@ import 'package:photos/utils/data_util.dart';
|
|||
import 'package:photos/utils/date_time_util.dart';
|
||||
|
||||
class SubscriptionHeaderWidget extends StatefulWidget {
|
||||
final bool isOnboarding;
|
||||
final int currentUsage;
|
||||
final bool? isOnboarding;
|
||||
final int? currentUsage;
|
||||
|
||||
const SubscriptionHeaderWidget({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.isOnboarding,
|
||||
this.currentUsage,
|
||||
}) : super(key: key);
|
||||
|
@ -26,7 +26,7 @@ class SubscriptionHeaderWidget extends StatefulWidget {
|
|||
class _SubscriptionHeaderWidgetState extends State<SubscriptionHeaderWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.isOnboarding) {
|
||||
if (widget.isOnboarding!) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 20, 20, 24),
|
||||
child: Column(
|
||||
|
@ -64,10 +64,10 @@ class _SubscriptionHeaderWidgetState extends State<SubscriptionHeaderWidget> {
|
|||
style: Theme.of(context).textTheme.subtitle1,
|
||||
),
|
||||
TextSpan(
|
||||
text: formatBytes(widget.currentUsage),
|
||||
text: formatBytes(widget.currentUsage!),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.subtitle1
|
||||
.subtitle1!
|
||||
.copyWith(fontWeight: FontWeight.bold),
|
||||
)
|
||||
],
|
||||
|
@ -80,9 +80,9 @@ class _SubscriptionHeaderWidgetState extends State<SubscriptionHeaderWidget> {
|
|||
}
|
||||
|
||||
class ValidityWidget extends StatelessWidget {
|
||||
final Subscription currentSubscription;
|
||||
final Subscription? currentSubscription;
|
||||
|
||||
const ValidityWidget({Key key, this.currentSubscription}) : super(key: key);
|
||||
const ValidityWidget({Key? key, this.currentSubscription}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -90,12 +90,12 @@ class ValidityWidget extends StatelessWidget {
|
|||
return const SizedBox.shrink();
|
||||
}
|
||||
final endDate = getDateAndMonthAndYear(
|
||||
DateTime.fromMicrosecondsSinceEpoch(currentSubscription.expiryTime),
|
||||
DateTime.fromMicrosecondsSinceEpoch(currentSubscription!.expiryTime),
|
||||
);
|
||||
var message = "Renews on $endDate";
|
||||
if (currentSubscription.productID == freeProductID) {
|
||||
if (currentSubscription!.productID == freeProductID) {
|
||||
message = "Free trial valid till $endDate";
|
||||
} else if (currentSubscription.attributes?.isCancelled ?? false) {
|
||||
} else if (currentSubscription!.attributes?.isCancelled ?? false) {
|
||||
message = "Your subscription will be cancelled on $endDate";
|
||||
}
|
||||
return Padding(
|
||||
|
@ -109,7 +109,7 @@ class ValidityWidget extends StatelessWidget {
|
|||
}
|
||||
|
||||
class SubFaqWidget extends StatelessWidget {
|
||||
const SubFaqWidget({Key key}) : super(key: key);
|
||||
const SubFaqWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -132,7 +132,7 @@ class SubFaqWidget extends StatelessWidget {
|
|||
child: RichText(
|
||||
text: TextSpan(
|
||||
text: "Questions?",
|
||||
style: Theme.of(context).textTheme.bodyMedium.copyWith(
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
@ -29,7 +29,7 @@ class SubscriptionPage extends StatefulWidget {
|
|||
|
||||
const SubscriptionPage({
|
||||
this.isOnboarding = false,
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -40,16 +40,16 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
|
|||
final _logger = Logger("SubscriptionPage");
|
||||
final _billingService = BillingService.instance;
|
||||
final _userService = UserService.instance;
|
||||
Subscription _currentSubscription;
|
||||
StreamSubscription _purchaseUpdateSubscription;
|
||||
ProgressDialog _dialog;
|
||||
UserDetails _userDetails;
|
||||
bool _hasActiveSubscription;
|
||||
FreePlan _freePlan;
|
||||
List<BillingPlan> _plans;
|
||||
Subscription? _currentSubscription;
|
||||
late StreamSubscription _purchaseUpdateSubscription;
|
||||
late ProgressDialog _dialog;
|
||||
late UserDetails _userDetails;
|
||||
late bool _hasActiveSubscription;
|
||||
late FreePlan _freePlan;
|
||||
late List<BillingPlan> _plans;
|
||||
bool _hasLoadedData = false;
|
||||
bool _isLoading = false;
|
||||
bool _isActiveStripeSubscriber;
|
||||
late bool _isActiveStripeSubscriber;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -76,9 +76,9 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
|
|||
String text = "Thank you for subscribing!";
|
||||
if (!widget.isOnboarding) {
|
||||
final isUpgrade = _hasActiveSubscription &&
|
||||
newSubscription.storage > _currentSubscription.storage;
|
||||
newSubscription.storage > _currentSubscription!.storage;
|
||||
final isDowngrade = _hasActiveSubscription &&
|
||||
newSubscription.storage < _currentSubscription.storage;
|
||||
newSubscription.storage < _currentSubscription!.storage;
|
||||
if (isUpgrade) {
|
||||
text = "Your plan was successfully upgraded";
|
||||
} else if (isDowngrade) {
|
||||
|
@ -87,7 +87,7 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
|
|||
}
|
||||
showShortToast(context, text);
|
||||
_currentSubscription = newSubscription;
|
||||
_hasActiveSubscription = _currentSubscription.isValid();
|
||||
_hasActiveSubscription = _currentSubscription!.isValid();
|
||||
setState(() {});
|
||||
await _dialog.hide();
|
||||
Bus.instance.fire(SubscriptionPurchasedEvent());
|
||||
|
@ -155,11 +155,11 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
|
|||
_userService.getUserDetailsV2(memoryCount: false).then((userDetails) async {
|
||||
_userDetails = userDetails;
|
||||
_currentSubscription = userDetails.subscription;
|
||||
_hasActiveSubscription = _currentSubscription.isValid();
|
||||
_hasActiveSubscription = _currentSubscription!.isValid();
|
||||
final billingPlans = await _billingService.getBillingPlans();
|
||||
_isActiveStripeSubscriber =
|
||||
_currentSubscription.paymentProvider == stripe &&
|
||||
_currentSubscription.isValid();
|
||||
_currentSubscription!.paymentProvider == stripe &&
|
||||
_currentSubscription!.isValid();
|
||||
_plans = billingPlans.plans.where((plan) {
|
||||
final productID = _isActiveStripeSubscriber
|
||||
? plan.stripeID
|
||||
|
@ -210,7 +210,7 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
|
|||
widgets.add(ValidityWidget(currentSubscription: _currentSubscription));
|
||||
}
|
||||
|
||||
if (_currentSubscription.productID == freeProductID) {
|
||||
if (_currentSubscription!.productID == freeProductID) {
|
||||
if (widget.isOnboarding) {
|
||||
widgets.add(SkipSubscriptionWidget(freePlan: _freePlan));
|
||||
}
|
||||
|
@ -218,20 +218,20 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
|
|||
}
|
||||
|
||||
if (_hasActiveSubscription &&
|
||||
_currentSubscription.productID != freeProductID) {
|
||||
_currentSubscription!.productID != freeProductID) {
|
||||
widgets.addAll([
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
final String paymentProvider =
|
||||
_currentSubscription.paymentProvider;
|
||||
_currentSubscription!.paymentProvider;
|
||||
if (paymentProvider == appStore && !Platform.isAndroid) {
|
||||
launchUrlString("https://apps.apple.com/account/billing");
|
||||
} else if (paymentProvider == playStore && Platform.isAndroid) {
|
||||
launchUrlString(
|
||||
"https://play.google.com/store/account/subscriptions?sku=" +
|
||||
_currentSubscription.productID +
|
||||
_currentSubscription!.productID +
|
||||
"&package=io.ente.photos",
|
||||
);
|
||||
} else if (paymentProvider == stripe) {
|
||||
|
@ -294,7 +294,7 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
|
|||
RichText(
|
||||
text: TextSpan(
|
||||
text: "Manage family",
|
||||
style: Theme.of(context).textTheme.bodyMedium.copyWith(
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
|
@ -324,7 +324,7 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
|
|||
continue;
|
||||
}
|
||||
final isActive =
|
||||
_hasActiveSubscription && _currentSubscription.productID == productID;
|
||||
_hasActiveSubscription && _currentSubscription!.productID == productID;
|
||||
if (isActive) {
|
||||
foundActivePlan = true;
|
||||
}
|
||||
|
@ -362,7 +362,7 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
|
|||
bool foundActivePlan = false;
|
||||
final List<Widget> planWidgets = [];
|
||||
if (_hasActiveSubscription &&
|
||||
_currentSubscription.productID == freeProductID) {
|
||||
_currentSubscription!.productID == freeProductID) {
|
||||
foundActivePlan = true;
|
||||
planWidgets.add(
|
||||
SubscriptionPlanWidget(
|
||||
|
@ -376,7 +376,7 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
|
|||
for (final plan in _plans) {
|
||||
final productID = Platform.isAndroid ? plan.androidID : plan.iosID;
|
||||
final isActive =
|
||||
_hasActiveSubscription && _currentSubscription.productID == productID;
|
||||
_hasActiveSubscription && _currentSubscription!.productID == productID;
|
||||
if (isActive) {
|
||||
foundActivePlan = true;
|
||||
}
|
||||
|
@ -408,8 +408,8 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
|
|||
}
|
||||
final isCrossGradingOnAndroid = Platform.isAndroid &&
|
||||
_hasActiveSubscription &&
|
||||
_currentSubscription.productID != freeProductID &&
|
||||
_currentSubscription.productID != plan.androidID;
|
||||
_currentSubscription!.productID != freeProductID &&
|
||||
_currentSubscription!.productID != plan.androidID;
|
||||
if (isCrossGradingOnAndroid) {
|
||||
await _dialog.hide();
|
||||
showErrorDialog(
|
||||
|
@ -445,7 +445,7 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
|
|||
void _addCurrentPlanWidget(List<Widget> planWidgets) {
|
||||
int activePlanIndex = 0;
|
||||
for (; activePlanIndex < _plans.length; activePlanIndex++) {
|
||||
if (_plans[activePlanIndex].storage > _currentSubscription.storage) {
|
||||
if (_plans[activePlanIndex].storage > _currentSubscription!.storage) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -455,9 +455,9 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
|
|||
child: InkWell(
|
||||
onTap: () {},
|
||||
child: SubscriptionPlanWidget(
|
||||
storage: _currentSubscription.storage,
|
||||
price: _currentSubscription.price,
|
||||
period: _currentSubscription.period,
|
||||
storage: _currentSubscription!.storage,
|
||||
price: _currentSubscription!.price,
|
||||
period: _currentSubscription!.period,
|
||||
isActive: true,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/utils/data_util.dart';
|
||||
|
||||
class SubscriptionPlanWidget extends StatelessWidget {
|
||||
const SubscriptionPlanWidget({
|
||||
Key key,
|
||||
@required this.storage,
|
||||
@required this.price,
|
||||
@required this.period,
|
||||
Key? key,
|
||||
required this.storage,
|
||||
required this.price,
|
||||
required this.period,
|
||||
this.isActive = false,
|
||||
}) : super(key: key);
|
||||
|
||||
|
@ -57,12 +57,12 @@ class SubscriptionPlanWidget extends StatelessWidget {
|
|||
convertBytesToReadableFormat(storage),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.headline6
|
||||
.headline6!
|
||||
.copyWith(color: textColor),
|
||||
),
|
||||
Text(
|
||||
_displayPrice(),
|
||||
style: Theme.of(context).textTheme.headline6.copyWith(
|
||||
style: Theme.of(context).textTheme.headline6!.copyWith(
|
||||
color: textColor,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
@ -11,9 +11,9 @@ import 'package:photos/theme/ente_theme.dart';
|
|||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
class AppUpdateDialog extends StatefulWidget {
|
||||
final LatestVersionInfo latestVersionInfo;
|
||||
final LatestVersionInfo? latestVersionInfo;
|
||||
|
||||
const AppUpdateDialog(this.latestVersionInfo, {Key key}) : super(key: key);
|
||||
const AppUpdateDialog(this.latestVersionInfo, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<AppUpdateDialog> createState() => _AppUpdateDialogState();
|
||||
|
@ -25,7 +25,7 @@ class _AppUpdateDialogState extends State<AppUpdateDialog> {
|
|||
final List<Widget> changelog = [];
|
||||
final enteTextTheme = getEnteTextTheme(context);
|
||||
final enteColor = getEnteColorScheme(context);
|
||||
for (final log in widget.latestVersionInfo.changelog) {
|
||||
for (final log in widget.latestVersionInfo!.changelog) {
|
||||
changelog.add(
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 4, 0, 4),
|
||||
|
@ -68,7 +68,7 @@ class _AppUpdateDialogState extends State<AppUpdateDialog> {
|
|||
width: double.infinity,
|
||||
height: 56,
|
||||
child: OutlinedButton(
|
||||
style: Theme.of(context).outlinedButtonTheme.style.copyWith(
|
||||
style: Theme.of(context).outlinedButtonTheme.style!.copyWith(
|
||||
textStyle: MaterialStateProperty.resolveWith<TextStyle>(
|
||||
(Set<MaterialState> states) {
|
||||
return enteTextTheme.bodyBold;
|
||||
|
@ -97,11 +97,11 @@ class _AppUpdateDialogState extends State<AppUpdateDialog> {
|
|||
"Install manually",
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.caption
|
||||
.caption!
|
||||
.copyWith(decoration: TextDecoration.underline),
|
||||
),
|
||||
onTap: () => launchUrlString(
|
||||
widget.latestVersionInfo.url,
|
||||
widget.latestVersionInfo!.url,
|
||||
mode: LaunchMode.externalApplication,
|
||||
),
|
||||
),
|
||||
|
@ -109,7 +109,7 @@ class _AppUpdateDialogState extends State<AppUpdateDialog> {
|
|||
],
|
||||
);
|
||||
final shouldForceUpdate =
|
||||
UpdateService.instance.shouldForceUpdate(widget.latestVersionInfo);
|
||||
UpdateService.instance.shouldForceUpdate(widget.latestVersionInfo!);
|
||||
return WillPopScope(
|
||||
onWillPop: () async => !shouldForceUpdate,
|
||||
child: AlertDialog(
|
||||
|
@ -139,24 +139,24 @@ class _AppUpdateDialogState extends State<AppUpdateDialog> {
|
|||
}
|
||||
|
||||
class ApkDownloaderDialog extends StatefulWidget {
|
||||
final LatestVersionInfo versionInfo;
|
||||
final LatestVersionInfo? versionInfo;
|
||||
|
||||
const ApkDownloaderDialog(this.versionInfo, {Key key}) : super(key: key);
|
||||
const ApkDownloaderDialog(this.versionInfo, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<ApkDownloaderDialog> createState() => _ApkDownloaderDialogState();
|
||||
}
|
||||
|
||||
class _ApkDownloaderDialogState extends State<ApkDownloaderDialog> {
|
||||
String _saveUrl;
|
||||
double _downloadProgress;
|
||||
String? _saveUrl;
|
||||
double? _downloadProgress;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_saveUrl = Configuration.instance.getTempDirectory() +
|
||||
"ente-" +
|
||||
widget.versionInfo.name +
|
||||
widget.versionInfo!.name +
|
||||
".apk";
|
||||
_downloadApk();
|
||||
}
|
||||
|
@ -186,11 +186,11 @@ class _ApkDownloaderDialogState extends State<ApkDownloaderDialog> {
|
|||
Future<void> _downloadApk() async {
|
||||
try {
|
||||
await Network.instance.getDio().download(
|
||||
widget.versionInfo.url,
|
||||
widget.versionInfo!.url,
|
||||
_saveUrl,
|
||||
onReceiveProgress: (count, _) {
|
||||
setState(() {
|
||||
_downloadProgress = count / widget.versionInfo.size;
|
||||
_downloadProgress = count / widget.versionInfo!.size;
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
|
@ -26,7 +26,7 @@ import 'package:photos/utils/toast_util.dart';
|
|||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
class BackupSectionWidget extends StatefulWidget {
|
||||
const BackupSectionWidget({Key key}) : super(key: key);
|
||||
const BackupSectionWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
BackupSectionWidgetState createState() => BackupSectionWidgetState();
|
||||
|
@ -108,7 +108,7 @@ class BackupSectionWidgetState extends State<BackupSectionWidget> {
|
|||
"You've no files on this device that can be deleted",
|
||||
);
|
||||
} else {
|
||||
final bool result =
|
||||
final bool? result =
|
||||
await routeToPage(context, FreeSpacePage(status));
|
||||
if (result == true) {
|
||||
_showSpaceFreedDialog(status);
|
||||
|
@ -145,7 +145,7 @@ class BackupSectionWidgetState extends State<BackupSectionWidget> {
|
|||
"You've no duplicate files that can be cleared",
|
||||
);
|
||||
} else {
|
||||
final DeduplicationResult result =
|
||||
final DeduplicationResult? result =
|
||||
await routeToPage(context, DeduplicatePage(duplicates));
|
||||
if (result != null) {
|
||||
_showDuplicateFilesDeletedDialog(result);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:adaptive_theme/adaptive_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -11,14 +11,14 @@ import 'package:photos/ui/components/menu_item_widget.dart';
|
|||
import 'package:photos/ui/settings/common_settings.dart';
|
||||
|
||||
class ThemeSwitchWidget extends StatefulWidget {
|
||||
const ThemeSwitchWidget({Key key}) : super(key: key);
|
||||
const ThemeSwitchWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<ThemeSwitchWidget> createState() => _ThemeSwitchWidgetState();
|
||||
}
|
||||
|
||||
class _ThemeSwitchWidgetState extends State<ThemeSwitchWidget> {
|
||||
AdaptiveThemeMode currentThemeMode;
|
||||
AdaptiveThemeMode? currentThemeMode;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -67,7 +67,7 @@ class _ThemeSwitchWidgetState extends State<ThemeSwitchWidget> {
|
|||
Widget _menuItem(BuildContext context, AdaptiveThemeMode themeMode) {
|
||||
return MenuItemWidget(
|
||||
captionedTextWidget: CaptionedTextWidget(
|
||||
title: toBeginningOfSentenceCase(themeMode.name),
|
||||
title: toBeginningOfSentenceCase(themeMode.name)!,
|
||||
textStyle: Theme.of(context).colorScheme.enteTheme.textTheme.body,
|
||||
),
|
||||
pressedColor: getEnteColorScheme(context).fillFaint,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
|
@ -23,8 +23,8 @@ import 'package:photos/ui/settings/support_section_widget.dart';
|
|||
import 'package:photos/ui/settings/theme_switch_widget.dart';
|
||||
|
||||
class SettingsPage extends StatelessWidget {
|
||||
final ValueNotifier<String> emailNotifier;
|
||||
const SettingsPage({Key key, @required this.emailNotifier}) : super(key: key);
|
||||
final ValueNotifier<String?> emailNotifier;
|
||||
const SettingsPage({Key? key, required this.emailNotifier}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -51,9 +51,9 @@ class SettingsPage extends StatelessWidget {
|
|||
child: AnimatedBuilder(
|
||||
// [AnimatedBuilder] accepts any [Listenable] subtype.
|
||||
animation: emailNotifier,
|
||||
builder: (BuildContext context, Widget child) {
|
||||
builder: (BuildContext context, Widget? child) {
|
||||
return Text(
|
||||
emailNotifier.value,
|
||||
emailNotifier.value!,
|
||||
style: enteTextTheme.body.copyWith(
|
||||
color: colorScheme.textMuted,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
@ -28,7 +28,7 @@ import 'package:photos/utils/share_util.dart';
|
|||
import 'package:photos/utils/toast_util.dart';
|
||||
|
||||
class SharedCollectionGallery extends StatefulWidget {
|
||||
const SharedCollectionGallery({Key key}) : super(key: key);
|
||||
const SharedCollectionGallery({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<SharedCollectionGallery> createState() =>
|
||||
|
@ -38,9 +38,9 @@ class SharedCollectionGallery extends StatefulWidget {
|
|||
class _SharedCollectionGalleryState extends State<SharedCollectionGallery>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
final Logger _logger = Logger("SharedCollectionGallery");
|
||||
StreamSubscription<LocalPhotosUpdatedEvent> _localFilesSubscription;
|
||||
StreamSubscription<CollectionUpdatedEvent> _collectionUpdatesSubscription;
|
||||
StreamSubscription<UserLoggedOutEvent> _loggedOutEvent;
|
||||
late StreamSubscription<LocalPhotosUpdatedEvent> _localFilesSubscription;
|
||||
late StreamSubscription<CollectionUpdatedEvent> _collectionUpdatesSubscription;
|
||||
late StreamSubscription<UserLoggedOutEvent> _loggedOutEvent;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -71,10 +71,10 @@ class _SharedCollectionGalleryState extends State<SharedCollectionGallery>
|
|||
final List<CollectionWithThumbnail> incoming = [];
|
||||
for (final file in files) {
|
||||
final c =
|
||||
CollectionsService.instance.getCollectionByID(file.collectionID);
|
||||
if (c.owner.id == Configuration.instance.getUserID()) {
|
||||
if (c.sharees.isNotEmpty ||
|
||||
c.publicURLs.isNotEmpty ||
|
||||
CollectionsService.instance.getCollectionByID(file.collectionID!)!;
|
||||
if (c.owner!.id == Configuration.instance.getUserID()) {
|
||||
if (c.sharees!.isNotEmpty ||
|
||||
c.publicURLs!.isNotEmpty ||
|
||||
c.isSharedFilesCollection()) {
|
||||
outgoing.add(
|
||||
CollectionWithThumbnail(
|
||||
|
@ -112,7 +112,7 @@ class _SharedCollectionGalleryState extends State<SharedCollectionGallery>
|
|||
}),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return _getSharedCollectionsGallery(snapshot.data);
|
||||
return _getSharedCollectionsGallery(snapshot.data!);
|
||||
} else if (snapshot.hasError) {
|
||||
_logger.shout(snapshot.error);
|
||||
return Center(child: Text(snapshot.error.toString()));
|
||||
|
@ -264,20 +264,20 @@ class OutgoingCollectionItem extends StatelessWidget {
|
|||
|
||||
const OutgoingCollectionItem(
|
||||
this.c, {
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final sharees = <String>[];
|
||||
for (int index = 0; index < c.collection.sharees.length; index++) {
|
||||
final sharee = c.collection.sharees[index];
|
||||
final sharees = <String?>[];
|
||||
for (int index = 0; index < c.collection.sharees!.length; index++) {
|
||||
final sharee = c.collection.sharees![index]!;
|
||||
final name =
|
||||
(sharee.name?.isNotEmpty ?? false) ? sharee.name : sharee.email;
|
||||
if (index < 2) {
|
||||
sharees.add(name);
|
||||
} else {
|
||||
final remaining = c.collection.sharees.length - index;
|
||||
final remaining = c.collection.sharees!.length - index;
|
||||
if (remaining == 1) {
|
||||
// If it's the last sharee
|
||||
sharees.add(name);
|
||||
|
@ -304,10 +304,10 @@ class OutgoingCollectionItem extends StatelessWidget {
|
|||
height: 60,
|
||||
width: 60,
|
||||
child: Hero(
|
||||
tag: "outgoing_collection" + c.thumbnail.tag,
|
||||
tag: "outgoing_collection" + c.thumbnail!.tag,
|
||||
child: ThumbnailWidget(
|
||||
c.thumbnail,
|
||||
key: Key("outgoing_collection" + c.thumbnail.tag),
|
||||
key: Key("outgoing_collection" + c.thumbnail!.tag),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -320,15 +320,15 @@ class OutgoingCollectionItem extends StatelessWidget {
|
|||
Row(
|
||||
children: [
|
||||
Text(
|
||||
c.collection.name,
|
||||
c.collection.name!,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
const Padding(padding: EdgeInsets.all(2)),
|
||||
c.collection.publicURLs.isEmpty
|
||||
c.collection.publicURLs!.isEmpty
|
||||
? Container()
|
||||
: (c.collection.publicURLs.first.isExpired
|
||||
: (c.collection.publicURLs!.first!.isExpired
|
||||
? const Icon(
|
||||
Icons.link,
|
||||
color: warning500,
|
||||
|
@ -373,7 +373,7 @@ class IncomingCollectionItem extends StatelessWidget {
|
|||
|
||||
const IncomingCollectionItem(
|
||||
this.c, {
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -381,7 +381,7 @@ class IncomingCollectionItem extends StatelessWidget {
|
|||
const double horizontalPaddingOfGridRow = 16;
|
||||
const double crossAxisSpacingOfGrid = 9;
|
||||
final TextStyle albumTitleTextStyle =
|
||||
Theme.of(context).textTheme.subtitle1.copyWith(fontSize: 14);
|
||||
Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 14);
|
||||
final Size size = MediaQuery.of(context).size;
|
||||
final int albumsCountInOneRow = max(size.width ~/ 220.0, 2);
|
||||
final double totalWhiteSpaceOfRow = (horizontalPaddingOfGridRow * 2) +
|
||||
|
@ -400,10 +400,10 @@ class IncomingCollectionItem extends StatelessWidget {
|
|||
child: Stack(
|
||||
children: [
|
||||
Hero(
|
||||
tag: "shared_collection" + c.thumbnail.tag,
|
||||
tag: "shared_collection" + c.thumbnail!.tag,
|
||||
child: ThumbnailWidget(
|
||||
c.thumbnail,
|
||||
key: Key("shared_collection" + c.thumbnail.tag),
|
||||
key: Key("shared_collection" + c.thumbnail!.tag),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
|
@ -411,7 +411,7 @@ class IncomingCollectionItem extends StatelessWidget {
|
|||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0, bottom: 8.0),
|
||||
child: UserAvatarWidget(
|
||||
c.collection.owner,
|
||||
c.collection.owner!,
|
||||
thumbnailView: true,
|
||||
),
|
||||
),
|
||||
|
@ -426,7 +426,7 @@ class IncomingCollectionItem extends StatelessWidget {
|
|||
Container(
|
||||
constraints: BoxConstraints(maxWidth: sideOfThumbnail - 40),
|
||||
child: Text(
|
||||
c.collection.name,
|
||||
c.collection.name!,
|
||||
style: albumTitleTextStyle,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
|
@ -434,11 +434,11 @@ class IncomingCollectionItem extends StatelessWidget {
|
|||
FutureBuilder<int>(
|
||||
future: FilesDB.instance.collectionFileCount(c.collection.id),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData && snapshot.data > 0) {
|
||||
if (snapshot.hasData && snapshot.data! > 0) {
|
||||
return RichText(
|
||||
text: TextSpan(
|
||||
style: albumTitleTextStyle.copyWith(
|
||||
color: albumTitleTextStyle.color.withOpacity(0.5),
|
||||
color: albumTitleTextStyle.color!.withOpacity(0.5),
|
||||
),
|
||||
children: [
|
||||
const TextSpan(text: " \u2022 "),
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// @dart=2.9
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
@ -25,9 +24,9 @@ import 'package:photos/utils/toast_util.dart';
|
|||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class ManageSharedLinkWidget extends StatefulWidget {
|
||||
final Collection collection;
|
||||
final Collection? collection;
|
||||
|
||||
const ManageSharedLinkWidget({Key key, this.collection}) : super(key: key);
|
||||
const ManageSharedLinkWidget({Key? key, this.collection}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<ManageSharedLinkWidget> createState() => _ManageSharedLinkWidgetState();
|
||||
|
@ -46,7 +45,7 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
|
|||
const Tuple3(6, "Custom", -1),
|
||||
];
|
||||
|
||||
Tuple3<int, String, int> _selectedExpiry;
|
||||
late Tuple3<int, String, int> _selectedExpiry;
|
||||
int _selectedDeviceLimitIndex = 0;
|
||||
final CollectionActions sharingActions =
|
||||
CollectionActions(CollectionsService.instance);
|
||||
|
@ -60,7 +59,7 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final enteColorScheme = getEnteColorScheme(context);
|
||||
final PublicURL url = widget.collection?.publicURLs?.firstOrNull;
|
||||
final PublicURL url = widget.collection!.publicURLs!.firstOrNull!;
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).backgroundColor,
|
||||
appBar: AppBar(
|
||||
|
@ -85,7 +84,7 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
|
|||
menuItemColor: getEnteColorScheme(context).fillFaint,
|
||||
pressedColor: getEnteColorScheme(context).fillFaint,
|
||||
trailingWidget: Switch.adaptive(
|
||||
value: widget.collection.publicURLs?.firstOrNull
|
||||
value: widget.collection!.publicURLs?.firstOrNull
|
||||
?.enableCollect ??
|
||||
false,
|
||||
onChanged: (value) async {
|
||||
|
@ -131,7 +130,8 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
|
|||
MenuItemWidget(
|
||||
captionedTextWidget: CaptionedTextWidget(
|
||||
title: "Device limit",
|
||||
subTitle: widget.collection.publicURLs.first.deviceLimit
|
||||
subTitle: widget
|
||||
.collection!.publicURLs!.first!.deviceLimit
|
||||
.toString(),
|
||||
),
|
||||
trailingIcon: Icons.chevron_right,
|
||||
|
@ -156,7 +156,7 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
|
|||
menuItemColor: getEnteColorScheme(context).fillFaint,
|
||||
pressedColor: getEnteColorScheme(context).fillFaint,
|
||||
trailingWidget: Switch.adaptive(
|
||||
value: widget.collection.publicURLs?.firstOrNull
|
||||
value: widget.collection!.publicURLs?.firstOrNull
|
||||
?.enableDownload ??
|
||||
true,
|
||||
onChanged: (value) async {
|
||||
|
@ -205,7 +205,7 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
|
|||
menuItemColor: getEnteColorScheme(context).fillFaint,
|
||||
pressedColor: getEnteColorScheme(context).fillFaint,
|
||||
trailingWidget: Switch.adaptive(
|
||||
value: widget.collection.publicURLs?.firstOrNull
|
||||
value: widget.collection!.publicURLs?.firstOrNull
|
||||
?.passwordEnabled ??
|
||||
false,
|
||||
onChanged: (enablePassword) async {
|
||||
|
@ -246,7 +246,7 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
|
|||
onTap: () async {
|
||||
final bool result = await sharingActions.publicLinkToggle(
|
||||
context,
|
||||
widget.collection,
|
||||
widget.collection!,
|
||||
false,
|
||||
);
|
||||
if (result && mounted) {
|
||||
|
@ -369,7 +369,7 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
|
|||
}
|
||||
|
||||
// _showDateTimePicker return null if user doesn't select date-time
|
||||
Future<int> _showDateTimePicker() async {
|
||||
Future<int?> _showDateTimePicker() async {
|
||||
final dateResult = await DatePicker.showDatePicker(
|
||||
context,
|
||||
minTime: DateTime.now(),
|
||||
|
@ -396,7 +396,7 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
|
|||
|
||||
final TextEditingController _textFieldController = TextEditingController();
|
||||
|
||||
Future<String> _displayLinkPasswordInput(BuildContext context) async {
|
||||
Future<String?> _displayLinkPasswordInput(BuildContext context) async {
|
||||
_textFieldController.clear();
|
||||
return showDialog<String>(
|
||||
context: context,
|
||||
|
@ -469,7 +469,7 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
|
|||
final int opsLimit = Sodium.cryptoPwhashOpslimitInteractive;
|
||||
final kekSalt = CryptoUtil.getSaltToDeriveKey();
|
||||
final result = await CryptoUtil.deriveKey(
|
||||
utf8.encode(pass),
|
||||
utf8.encode(pass) as Uint8List,
|
||||
kekSalt,
|
||||
memLimit,
|
||||
opsLimit,
|
||||
|
@ -489,7 +489,8 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
|
|||
final dialog = createProgressDialog(context, "Please wait...");
|
||||
await dialog.show();
|
||||
try {
|
||||
await CollectionsService.instance.updateShareUrl(widget.collection, prop);
|
||||
await CollectionsService.instance
|
||||
.updateShareUrl(widget.collection!, prop);
|
||||
await dialog.hide();
|
||||
showShortToast(context, "Album updated");
|
||||
} catch (e) {
|
||||
|
@ -500,7 +501,7 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
|
|||
|
||||
Text _getLinkExpiryTimeWidget() {
|
||||
final int validTill =
|
||||
widget.collection.publicURLs?.firstOrNull?.validTill ?? 0;
|
||||
widget.collection!.publicURLs?.firstOrNull?.validTill ?? 0;
|
||||
if (validTill == 0) {
|
||||
return const Text(
|
||||
'Never',
|
||||
|
@ -568,7 +569,7 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
|
|||
onPressed: () async {
|
||||
await _updateUrlSettings(context, {
|
||||
'deviceLimit': int.tryParse(
|
||||
options[_selectedDeviceLimitIndex].data,
|
||||
options[_selectedDeviceLimitIndex].data!,
|
||||
),
|
||||
});
|
||||
setState(() {});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
|
@ -27,24 +27,24 @@ import 'package:flutter/material.dart';
|
|||
|
||||
// ignore_for_file: unnecessary_this, library_private_types_in_public_api
|
||||
class AppLock extends StatefulWidget {
|
||||
final Widget Function(Object) builder;
|
||||
final Widget Function(Object?) builder;
|
||||
final Widget lockScreen;
|
||||
final bool enabled;
|
||||
final Duration backgroundLockLatency;
|
||||
final ThemeData darkTheme;
|
||||
final ThemeData lightTheme;
|
||||
final ThemeData? darkTheme;
|
||||
final ThemeData? lightTheme;
|
||||
|
||||
const AppLock({
|
||||
Key key,
|
||||
@required this.builder,
|
||||
@required this.lockScreen,
|
||||
Key? key,
|
||||
required this.builder,
|
||||
required this.lockScreen,
|
||||
this.enabled = true,
|
||||
this.backgroundLockLatency = const Duration(seconds: 0),
|
||||
this.darkTheme,
|
||||
this.lightTheme,
|
||||
}) : super(key: key);
|
||||
|
||||
static _AppLockState of(BuildContext context) =>
|
||||
static _AppLockState? of(BuildContext context) =>
|
||||
context.findAncestorStateOfType<_AppLockState>();
|
||||
|
||||
@override
|
||||
|
@ -54,11 +54,11 @@ class AppLock extends StatefulWidget {
|
|||
class _AppLockState extends State<AppLock> with WidgetsBindingObserver {
|
||||
static final GlobalKey<NavigatorState> _navigatorKey = GlobalKey();
|
||||
|
||||
bool _didUnlockForAppLaunch;
|
||||
bool _isLocked;
|
||||
bool _enabled;
|
||||
late bool _didUnlockForAppLaunch;
|
||||
late bool _isLocked;
|
||||
late bool _enabled;
|
||||
|
||||
Timer _backgroundLockLatencyTimer;
|
||||
Timer? _backgroundLockLatencyTimer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -139,7 +139,7 @@ class _AppLockState extends State<AppLock> with WidgetsBindingObserver {
|
|||
/// when built. Use this when you want to inject objects created from the
|
||||
/// [lockScreen] in to the rest of your app so you can better guarantee that some
|
||||
/// objects, services or databases are already instantiated before using them.
|
||||
void didUnlock([Object args]) {
|
||||
void didUnlock([Object? args]) {
|
||||
if (this._didUnlockForAppLaunch) {
|
||||
this._didUnlockOnAppPaused();
|
||||
} else {
|
||||
|
@ -178,17 +178,17 @@ class _AppLockState extends State<AppLock> with WidgetsBindingObserver {
|
|||
/// Manually show the [lockScreen].
|
||||
Future<void> showLockScreen() {
|
||||
this._isLocked = true;
|
||||
return _navigatorKey.currentState.pushNamed('/lock-screen');
|
||||
return _navigatorKey.currentState!.pushNamed('/lock-screen');
|
||||
}
|
||||
|
||||
void _didUnlockOnAppLaunch(Object args) {
|
||||
void _didUnlockOnAppLaunch(Object? args) {
|
||||
this._didUnlockForAppLaunch = true;
|
||||
_navigatorKey.currentState
|
||||
_navigatorKey.currentState!
|
||||
.pushReplacementNamed('/unlocked', arguments: args);
|
||||
}
|
||||
|
||||
void _didUnlockOnAppPaused() {
|
||||
this._isLocked = false;
|
||||
_navigatorKey.currentState.pop();
|
||||
_navigatorKey.currentState!.pop();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:io';
|
||||
import 'dart:ui';
|
||||
|
@ -8,14 +8,14 @@ import 'package:photos/ui/common/loading_widget.dart';
|
|||
|
||||
class LogFileViewer extends StatefulWidget {
|
||||
final File file;
|
||||
const LogFileViewer(this.file, {Key key}) : super(key: key);
|
||||
const LogFileViewer(this.file, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<LogFileViewer> createState() => _LogFileViewerState();
|
||||
}
|
||||
|
||||
class _LogFileViewerState extends State<LogFileViewer> {
|
||||
String _logs;
|
||||
String? _logs;
|
||||
@override
|
||||
void initState() {
|
||||
widget.file.readAsString().then((logs) {
|
||||
|
@ -45,7 +45,7 @@ class _LogFileViewerState extends State<LogFileViewer> {
|
|||
padding: const EdgeInsets.only(left: 12, top: 8, right: 12),
|
||||
child: SingleChildScrollView(
|
||||
child: Text(
|
||||
_logs,
|
||||
_logs!,
|
||||
style: const TextStyle(
|
||||
fontFeatures: [
|
||||
FontFeature.tabularFigures(),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
@ -21,7 +21,7 @@ import 'package:photos/utils/toast_util.dart';
|
|||
class DeduplicatePage extends StatefulWidget {
|
||||
final List<DuplicateFiles> duplicates;
|
||||
|
||||
const DeduplicatePage(this.duplicates, {Key key}) : super(key: key);
|
||||
const DeduplicatePage(this.duplicates, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<DeduplicatePage> createState() => _DeduplicatePageState();
|
||||
|
@ -47,9 +47,9 @@ class _DeduplicatePageState extends State<DeduplicatePage> {
|
|||
);
|
||||
|
||||
final Set<File> _selectedFiles = <File>{};
|
||||
final Map<int, int> _fileSizeMap = {};
|
||||
List<DuplicateFiles> _duplicates;
|
||||
bool _shouldClubByCaptureTime = true;
|
||||
final Map<int?, int> _fileSizeMap = {};
|
||||
late List<DuplicateFiles> _duplicates;
|
||||
bool? _shouldClubByCaptureTime = true;
|
||||
|
||||
SortKey sortKey = SortKey.size;
|
||||
|
||||
|
@ -91,7 +91,7 @@ class _DeduplicatePageState extends State<DeduplicatePage> {
|
|||
Radius.circular(8),
|
||||
),
|
||||
),
|
||||
onSelected: (value) {
|
||||
onSelected: (dynamic value) {
|
||||
setState(() {
|
||||
_selectedFiles.clear();
|
||||
});
|
||||
|
@ -114,7 +114,7 @@ class _DeduplicatePageState extends State<DeduplicatePage> {
|
|||
"Deselect all",
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.subtitle1
|
||||
.subtitle1!
|
||||
.copyWith(fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
|
@ -138,7 +138,7 @@ class _DeduplicatePageState extends State<DeduplicatePage> {
|
|||
} else if (sortKey == SortKey.count) {
|
||||
return second.files.length - first.files.length;
|
||||
} else {
|
||||
return second.files.first.creationTime - first.files.first.creationTime;
|
||||
return second.files.first.creationTime! - first.files.first.creationTime!;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -197,7 +197,7 @@ class _DeduplicatePageState extends State<DeduplicatePage> {
|
|||
children: [
|
||||
Text(
|
||||
"Following files were clubbed based on their sizes" +
|
||||
((_shouldClubByCaptureTime ? " and capture times." : ".")),
|
||||
(_shouldClubByCaptureTime! ? " and capture times." : "."),
|
||||
style: Theme.of(context).textTheme.subtitle2,
|
||||
),
|
||||
const Padding(
|
||||
|
@ -234,7 +234,7 @@ class _DeduplicatePageState extends State<DeduplicatePage> {
|
|||
}
|
||||
|
||||
void _resetEntriesAndSelection() {
|
||||
if (_shouldClubByCaptureTime) {
|
||||
if (_shouldClubByCaptureTime!) {
|
||||
_duplicates =
|
||||
DeduplicationService.instance.clubDuplicatesByTime(_duplicates);
|
||||
} else {
|
||||
|
@ -259,9 +259,9 @@ class _DeduplicatePageState extends State<DeduplicatePage> {
|
|||
}
|
||||
return Text(
|
||||
text,
|
||||
style: Theme.of(context).textTheme.subtitle1.copyWith(
|
||||
style: Theme.of(context).textTheme.subtitle1!.copyWith(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).iconTheme.color.withOpacity(0.7),
|
||||
color: Theme.of(context).iconTheme.color!.withOpacity(0.7),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -320,7 +320,7 @@ class _DeduplicatePageState extends State<DeduplicatePage> {
|
|||
}
|
||||
int size = 0;
|
||||
for (final file in _selectedFiles) {
|
||||
size += _fileSizeMap[file.uploadedFileID];
|
||||
size += _fileSizeMap[file.uploadedFileID]!;
|
||||
}
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
|
@ -471,9 +471,9 @@ class _DeduplicatePageState extends State<DeduplicatePage> {
|
|||
padding: const EdgeInsets.only(right: 2),
|
||||
child: Text(
|
||||
CollectionsService.instance
|
||||
.getCollectionByID(file.collectionID)
|
||||
.name,
|
||||
style: Theme.of(context).textTheme.caption.copyWith(fontSize: 12),
|
||||
.getCollectionByID(file.collectionID!)!
|
||||
.name!,
|
||||
style: Theme.of(context).textTheme.caption!.copyWith(fontSize: 12),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:math';
|
||||
|
||||
|
@ -7,14 +7,14 @@ import 'package:image_editor/image_editor.dart';
|
|||
|
||||
class FilteredImage extends StatelessWidget {
|
||||
const FilteredImage({
|
||||
@required this.child,
|
||||
required this.child,
|
||||
this.brightness,
|
||||
this.saturation,
|
||||
this.hue,
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
final double brightness, saturation, hue;
|
||||
final double? brightness, saturation, hue;
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// @dart=2.9
|
||||
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
@ -38,7 +36,7 @@ class ImageEditorPage extends StatefulWidget {
|
|||
this.imageProvider,
|
||||
this.originalFile,
|
||||
this.detailPageConfig, {
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -57,8 +55,8 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
|
|||
final GlobalKey<ExtendedImageEditorState> editorKey =
|
||||
GlobalKey<ExtendedImageEditorState>();
|
||||
|
||||
double _brightness = kBrightnessDefault;
|
||||
double _saturation = kSaturationDefault;
|
||||
double? _brightness = kBrightnessDefault;
|
||||
double? _saturation = kSaturationDefault;
|
||||
bool _hasEdited = false;
|
||||
|
||||
@override
|
||||
|
@ -81,7 +79,7 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
|
|||
IconButton(
|
||||
padding: const EdgeInsets.only(right: 16, left: 16),
|
||||
onPressed: () {
|
||||
editorKey.currentState.reset();
|
||||
editorKey.currentState!.reset();
|
||||
setState(() {
|
||||
_brightness = kBrightnessDefault;
|
||||
_saturation = kSaturationDefault;
|
||||
|
@ -163,7 +161,7 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
|
|||
}
|
||||
|
||||
Widget _buildFlipButton() {
|
||||
final TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2;
|
||||
final TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2!;
|
||||
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
|
@ -178,7 +176,7 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
|
|||
padding: const EdgeInsets.only(bottom: 2),
|
||||
child: Icon(
|
||||
Icons.flip,
|
||||
color: Theme.of(context).iconTheme.color.withOpacity(0.8),
|
||||
color: Theme.of(context).iconTheme.color!.withOpacity(0.8),
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
|
@ -186,7 +184,7 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
|
|||
Text(
|
||||
"Flip",
|
||||
style: subtitle2.copyWith(
|
||||
color: subtitle2.color.withOpacity(0.8),
|
||||
color: subtitle2.color!.withOpacity(0.8),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
|
@ -197,7 +195,7 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
|
|||
}
|
||||
|
||||
Widget _buildRotateLeftButton() {
|
||||
final TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2;
|
||||
final TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2!;
|
||||
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
|
@ -210,13 +208,13 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
|
|||
children: [
|
||||
Icon(
|
||||
Icons.rotate_left,
|
||||
color: Theme.of(context).iconTheme.color.withOpacity(0.8),
|
||||
color: Theme.of(context).iconTheme.color!.withOpacity(0.8),
|
||||
),
|
||||
const Padding(padding: EdgeInsets.all(2)),
|
||||
Text(
|
||||
"Rotate left",
|
||||
style: subtitle2.copyWith(
|
||||
color: subtitle2.color.withOpacity(0.8),
|
||||
color: subtitle2.color!.withOpacity(0.8),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
|
@ -227,7 +225,7 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
|
|||
}
|
||||
|
||||
Widget _buildRotateRightButton() {
|
||||
final TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2;
|
||||
final TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2!;
|
||||
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
|
@ -240,13 +238,13 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
|
|||
children: [
|
||||
Icon(
|
||||
Icons.rotate_right,
|
||||
color: Theme.of(context).iconTheme.color.withOpacity(0.8),
|
||||
color: Theme.of(context).iconTheme.color!.withOpacity(0.8),
|
||||
),
|
||||
const Padding(padding: EdgeInsets.all(2)),
|
||||
Text(
|
||||
"Rotate right",
|
||||
style: subtitle2.copyWith(
|
||||
color: subtitle2.color.withOpacity(0.8),
|
||||
color: subtitle2.color!.withOpacity(0.8),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
|
@ -257,7 +255,7 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
|
|||
}
|
||||
|
||||
Widget _buildSaveButton() {
|
||||
final TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2;
|
||||
final TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2!;
|
||||
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
|
@ -270,13 +268,13 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
|
|||
children: [
|
||||
Icon(
|
||||
Icons.save_alt_outlined,
|
||||
color: Theme.of(context).iconTheme.color.withOpacity(0.8),
|
||||
color: Theme.of(context).iconTheme.color!.withOpacity(0.8),
|
||||
),
|
||||
const Padding(padding: EdgeInsets.all(2)),
|
||||
Text(
|
||||
"Save copy",
|
||||
style: subtitle2.copyWith(
|
||||
color: subtitle2.color.withOpacity(0.8),
|
||||
color: subtitle2.color!.withOpacity(0.8),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
|
@ -289,15 +287,15 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
|
|||
Future<void> _saveEdits() async {
|
||||
final dialog = createProgressDialog(context, "Saving...");
|
||||
await dialog.show();
|
||||
final ExtendedImageEditorState state = editorKey.currentState;
|
||||
final ExtendedImageEditorState? state = editorKey.currentState;
|
||||
if (state == null) {
|
||||
return;
|
||||
}
|
||||
final Rect rect = state.getCropRect();
|
||||
final Rect? rect = state.getCropRect();
|
||||
if (rect == null) {
|
||||
return;
|
||||
}
|
||||
final EditActionDetails action = state.editAction;
|
||||
final EditActionDetails action = state.editAction!;
|
||||
final double radian = action.rotateAngle;
|
||||
|
||||
final bool flipHorizontal = action.flipY;
|
||||
|
@ -320,13 +318,13 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
|
|||
option.addOption(RotateOption(radian.toInt()));
|
||||
}
|
||||
|
||||
option.addOption(ColorOption.saturation(_saturation));
|
||||
option.addOption(ColorOption.brightness(_brightness));
|
||||
option.addOption(ColorOption.saturation(_saturation!));
|
||||
option.addOption(ColorOption.brightness(_brightness!));
|
||||
|
||||
option.outputFormat = const OutputFormat.png(88);
|
||||
|
||||
final DateTime start = DateTime.now();
|
||||
final Uint8List result = await ImageEditor.editImage(
|
||||
final Uint8List? result = await ImageEditor.editImage(
|
||||
image: img,
|
||||
imageEditorOption: option,
|
||||
);
|
||||
|
@ -341,16 +339,14 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
|
|||
}
|
||||
try {
|
||||
final fileName =
|
||||
path.basenameWithoutExtension(widget.originalFile.title) +
|
||||
path.basenameWithoutExtension(widget.originalFile.title!) +
|
||||
"_edited_" +
|
||||
DateTime.now().microsecondsSinceEpoch.toString() +
|
||||
path.extension(widget.originalFile.title);
|
||||
final newAsset = await PhotoManager.editor.saveImage(
|
||||
result,
|
||||
title: fileName,
|
||||
);
|
||||
final newFile =
|
||||
await ente.File.fromAsset(widget.originalFile.deviceFolder, newAsset);
|
||||
path.extension(widget.originalFile.title!);
|
||||
final AssetEntity? newAsset =
|
||||
await (PhotoManager.editor.saveImage(result, title: fileName));
|
||||
final newFile = await ente.File.fromAsset(
|
||||
widget.originalFile.deviceFolder!, newAsset!);
|
||||
newFile.creationTime = widget.originalFile.creationTime;
|
||||
newFile.collectionID = widget.originalFile.collectionID;
|
||||
newFile.location = widget.originalFile.location;
|
||||
|
@ -369,9 +365,9 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
|
|||
_logger.info("Original file " + widget.originalFile.toString());
|
||||
_logger.info("Saved edits to file " + newFile.toString());
|
||||
final existingFiles = widget.detailPageConfig.files;
|
||||
final files = (await widget.detailPageConfig.asyncLoader(
|
||||
existingFiles[existingFiles.length - 1].creationTime,
|
||||
existingFiles[0].creationTime,
|
||||
final files = (await widget.detailPageConfig.asyncLoader!(
|
||||
existingFiles[existingFiles.length - 1].creationTime!,
|
||||
existingFiles[0].creationTime!,
|
||||
))
|
||||
.files;
|
||||
// the index could be -1 if the files fetched doesn't contain the newly
|
||||
|
@ -406,7 +402,7 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
|
|||
}
|
||||
|
||||
Widget _buildSat() {
|
||||
final TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2;
|
||||
final TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2!;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||
|
@ -417,7 +413,7 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
|
|||
child: Text(
|
||||
"Color",
|
||||
style: subtitle2.copyWith(
|
||||
color: subtitle2.color.withOpacity(0.8),
|
||||
color: subtitle2.color!.withOpacity(0.8),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -452,7 +448,7 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
|
|||
}
|
||||
|
||||
Widget _buildBrightness() {
|
||||
final TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2;
|
||||
final TextStyle subtitle2 = Theme.of(context).textTheme.subtitle2!;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||
|
@ -463,7 +459,7 @@ class _ImageEditorPageState extends State<ImageEditorPage> {
|
|||
child: Text(
|
||||
"Light",
|
||||
style: subtitle2.copyWith(
|
||||
color: subtitle2.color.withOpacity(0.8),
|
||||
color: subtitle2.color!.withOpacity(0.8),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
@ -14,7 +14,7 @@ class FreeSpacePage extends StatefulWidget {
|
|||
|
||||
const FreeSpacePage(
|
||||
this.status, {
|
||||
Key key,
|
||||
Key? key,
|
||||
this.clearSpaceForFolder = false,
|
||||
}) : super(key: key);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
@ -7,7 +7,7 @@ import 'package:photos/ui/tools/app_lock.dart';
|
|||
import 'package:photos/utils/auth_util.dart';
|
||||
|
||||
class LockScreen extends StatefulWidget {
|
||||
const LockScreen({Key key}) : super(key: key);
|
||||
const LockScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<LockScreen> createState() => _LockScreenState();
|
||||
|
@ -100,7 +100,7 @@ class _LockScreenState extends State<LockScreen> with WidgetsBindingObserver {
|
|||
);
|
||||
_isShowingLockScreen = false;
|
||||
if (result) {
|
||||
AppLock.of(context).didUnlock();
|
||||
AppLock.of(context)!.didUnlock();
|
||||
} else {
|
||||
_logger.info("Dismissed");
|
||||
if (!_hasPlacedAppInBackground) {
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
// @dart=2.9
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomAppBar extends PreferredSize {
|
||||
@override
|
||||
final Widget child;
|
||||
@override
|
||||
final Size preferredSize;
|
||||
final double height;
|
||||
|
||||
const CustomAppBar(this.child, {Key key, this.height = kToolbarHeight})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Size get preferredSize => Size.fromHeight(height);
|
||||
const CustomAppBar(
|
||||
this.child,
|
||||
this.preferredSize, {
|
||||
Key? key,
|
||||
this.height = kToolbarHeight,
|
||||
}) : super(key: key, child: child, preferredSize: preferredSize);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// @dart=2.9
|
||||
|
||||
import 'package:extended_image/extended_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
@ -24,7 +22,7 @@ enum DetailPageMode {
|
|||
|
||||
class DetailPageConfiguration {
|
||||
final List<File> files;
|
||||
final GalleryLoader asyncLoader;
|
||||
final GalleryLoader? asyncLoader;
|
||||
final int selectedIndex;
|
||||
final String tagPrefix;
|
||||
final DetailPageMode mode;
|
||||
|
@ -38,10 +36,10 @@ class DetailPageConfiguration {
|
|||
});
|
||||
|
||||
DetailPageConfiguration copyWith({
|
||||
List<File> files,
|
||||
GalleryLoader asyncLoader,
|
||||
int selectedIndex,
|
||||
String tagPrefix,
|
||||
List<File>? files,
|
||||
GalleryLoader? asyncLoader,
|
||||
int? selectedIndex,
|
||||
String? tagPrefix,
|
||||
}) {
|
||||
return DetailPageConfiguration(
|
||||
files ?? this.files,
|
||||
|
@ -65,15 +63,15 @@ class _DetailPageState extends State<DetailPage> {
|
|||
static const kLoadLimit = 100;
|
||||
final _logger = Logger("DetailPageState");
|
||||
bool _shouldDisableScroll = false;
|
||||
List<File> _files;
|
||||
PageController _pageController;
|
||||
List<File>? _files;
|
||||
PageController? _pageController;
|
||||
int _selectedIndex = 0;
|
||||
bool _hasPageChanged = false;
|
||||
bool _hasLoadedTillStart = false;
|
||||
bool _hasLoadedTillEnd = false;
|
||||
bool _shouldHideAppBar = false;
|
||||
GlobalKey<FadingAppBarState> _appBarKey;
|
||||
GlobalKey<FadingBottomBarState> _bottomBarKey;
|
||||
GlobalKey<FadingAppBarState>? _appBarKey;
|
||||
GlobalKey<FadingBottomBarState>? _bottomBarKey;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -98,18 +96,18 @@ class _DetailPageState extends State<DetailPage> {
|
|||
Widget build(BuildContext context) {
|
||||
_logger.info(
|
||||
"Opening " +
|
||||
_files[_selectedIndex].toString() +
|
||||
_files![_selectedIndex].toString() +
|
||||
". " +
|
||||
(_selectedIndex + 1).toString() +
|
||||
" / " +
|
||||
_files.length.toString() +
|
||||
_files!.length.toString() +
|
||||
" files .",
|
||||
);
|
||||
_appBarKey = GlobalKey<FadingAppBarState>();
|
||||
_bottomBarKey = GlobalKey<FadingBottomBarState>();
|
||||
return Scaffold(
|
||||
appBar: FadingAppBar(
|
||||
_files[_selectedIndex],
|
||||
_files![_selectedIndex],
|
||||
_onFileRemoved,
|
||||
Configuration.instance.getUserID(),
|
||||
100,
|
||||
|
@ -122,7 +120,7 @@ class _DetailPageState extends State<DetailPage> {
|
|||
children: [
|
||||
_buildPageView(),
|
||||
FadingBottomBar(
|
||||
_files[_selectedIndex],
|
||||
_files![_selectedIndex],
|
||||
_onEditFileRequested,
|
||||
widget.config.mode == DetailPageMode.minimalistic,
|
||||
key: _bottomBarKey,
|
||||
|
@ -140,7 +138,7 @@ class _DetailPageState extends State<DetailPage> {
|
|||
_pageController = PageController(initialPage: _selectedIndex);
|
||||
return PageView.builder(
|
||||
itemBuilder: (context, index) {
|
||||
final file = _files[index];
|
||||
final file = _files![index];
|
||||
final Widget content = FileWidget(
|
||||
file,
|
||||
autoPlay: !_hasPageChanged,
|
||||
|
@ -181,17 +179,17 @@ class _DetailPageState extends State<DetailPage> {
|
|||
? const NeverScrollableScrollPhysics()
|
||||
: const PageScrollPhysics(),
|
||||
controller: _pageController,
|
||||
itemCount: _files.length,
|
||||
itemCount: _files!.length,
|
||||
);
|
||||
}
|
||||
|
||||
void _toggleFullScreen() {
|
||||
if (_shouldHideAppBar) {
|
||||
_appBarKey.currentState.hide();
|
||||
_bottomBarKey.currentState.hide();
|
||||
_appBarKey!.currentState!.hide();
|
||||
_bottomBarKey!.currentState!.hide();
|
||||
} else {
|
||||
_appBarKey.currentState.show();
|
||||
_bottomBarKey.currentState.show();
|
||||
_appBarKey!.currentState!.show();
|
||||
_bottomBarKey!.currentState!.show();
|
||||
}
|
||||
Future.delayed(Duration.zero, () {
|
||||
SystemChrome.setEnabledSystemUIMode(
|
||||
|
@ -207,8 +205,8 @@ class _DetailPageState extends State<DetailPage> {
|
|||
return;
|
||||
}
|
||||
if (_selectedIndex == 0 && !_hasLoadedTillStart) {
|
||||
final result = await widget.config.asyncLoader(
|
||||
_files[_selectedIndex].creationTime + 1,
|
||||
final result = await widget.config.asyncLoader!(
|
||||
_files![_selectedIndex].creationTime! + 1,
|
||||
DateTime.now().microsecondsSinceEpoch,
|
||||
limit: kLoadLimit,
|
||||
asc: true,
|
||||
|
@ -221,38 +219,38 @@ class _DetailPageState extends State<DetailPage> {
|
|||
_hasLoadedTillStart = true;
|
||||
}
|
||||
final length = files.length;
|
||||
files.addAll(_files);
|
||||
files.addAll(_files!);
|
||||
_files = files;
|
||||
_pageController.jumpToPage(length);
|
||||
_pageController!.jumpToPage(length);
|
||||
_selectedIndex = length;
|
||||
});
|
||||
}
|
||||
if (_selectedIndex == _files.length - 1 && !_hasLoadedTillEnd) {
|
||||
final result = await widget.config.asyncLoader(
|
||||
if (_selectedIndex == _files!.length - 1 && !_hasLoadedTillEnd) {
|
||||
final result = await widget.config.asyncLoader!(
|
||||
galleryLoadStartTime,
|
||||
_files[_selectedIndex].creationTime - 1,
|
||||
_files![_selectedIndex].creationTime! - 1,
|
||||
limit: kLoadLimit,
|
||||
);
|
||||
setState(() {
|
||||
if (!result.hasMore) {
|
||||
_hasLoadedTillEnd = true;
|
||||
}
|
||||
_files.addAll(result.files);
|
||||
_files!.addAll(result.files);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _preloadFiles(int index) {
|
||||
if (index > 0) {
|
||||
preloadFile(_files[index - 1]);
|
||||
preloadFile(_files![index - 1]);
|
||||
}
|
||||
if (index < _files.length - 1) {
|
||||
preloadFile(_files[index + 1]);
|
||||
if (index < _files!.length - 1) {
|
||||
preloadFile(_files![index + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onFileRemoved(File file) async {
|
||||
final totalFiles = _files.length;
|
||||
final totalFiles = _files!.length;
|
||||
if (totalFiles == 1) {
|
||||
// Deleted the only file
|
||||
Navigator.of(context).pop(); // Close pageview
|
||||
|
@ -260,21 +258,21 @@ class _DetailPageState extends State<DetailPage> {
|
|||
}
|
||||
if (_selectedIndex == totalFiles - 1) {
|
||||
// Deleted the last file
|
||||
await _pageController.previousPage(
|
||||
await _pageController!.previousPage(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
setState(() {
|
||||
_files.remove(file);
|
||||
_files!.remove(file);
|
||||
});
|
||||
} else {
|
||||
await _pageController.nextPage(
|
||||
await _pageController!.nextPage(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
setState(() {
|
||||
_selectedIndex--;
|
||||
_files.remove(file);
|
||||
_files!.remove(file);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -297,7 +295,7 @@ class _DetailPageState extends State<DetailPage> {
|
|||
final dialog = createProgressDialog(context, "Please wait...");
|
||||
await dialog.show();
|
||||
final imageProvider =
|
||||
ExtendedFileImageProvider(await getFile(file), cacheRawData: true);
|
||||
ExtendedFileImageProvider((await getFile(file))!, cacheRawData: true);
|
||||
await precacheImage(imageProvider, context);
|
||||
await dialog.hide();
|
||||
replacePage(
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// @dart=2.9
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/ui/common/loading_widget.dart';
|
||||
|
@ -7,16 +5,16 @@ import 'package:photos/ui/viewer/file/file_info_collection_widget.dart';
|
|||
|
||||
class DeviceFoldersListOfFileWidget extends StatelessWidget {
|
||||
final Future<Set<String>> allDeviceFoldersOfFile;
|
||||
const DeviceFoldersListOfFileWidget(this.allDeviceFoldersOfFile, {Key key})
|
||||
const DeviceFoldersListOfFileWidget(this.allDeviceFoldersOfFile, {Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
return FutureBuilder<Set<String>>(
|
||||
future: allDeviceFoldersOfFile,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
final List<String> deviceFolders = snapshot.data.toList();
|
||||
final List<String> deviceFolders = snapshot.data!.toList();
|
||||
return ListView.builder(
|
||||
itemCount: deviceFolders.length,
|
||||
scrollDirection: Axis.horizontal,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:ui';
|
||||
|
||||
|
@ -9,7 +9,7 @@ import 'package:photos/utils/exif_util.dart';
|
|||
|
||||
class ExifInfoDialog extends StatefulWidget {
|
||||
final File file;
|
||||
const ExifInfoDialog(this.file, {Key key}) : super(key: key);
|
||||
const ExifInfoDialog(this.file, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<ExifInfoDialog> createState() => _ExifInfoDialogState();
|
||||
|
@ -21,7 +21,7 @@ class _ExifInfoDialogState extends State<ExifInfoDialog> {
|
|||
final scrollController = ScrollController();
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
widget.file.title,
|
||||
widget.file.title!,
|
||||
style: Theme.of(context).textTheme.headline5,
|
||||
),
|
||||
content: Scrollbar(
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// @dart=2.9
|
||||
|
||||
import 'dart:io';
|
||||
import 'dart:io' as io;
|
||||
|
||||
|
@ -37,7 +35,7 @@ class FadingAppBar extends StatefulWidget implements PreferredSizeWidget {
|
|||
final Function(File) onFileRemoved;
|
||||
final double height;
|
||||
final bool shouldShowActions;
|
||||
final int userID;
|
||||
final int? userID;
|
||||
|
||||
const FadingAppBar(
|
||||
this.file,
|
||||
|
@ -45,7 +43,7 @@ class FadingAppBar extends StatefulWidget implements PreferredSizeWidget {
|
|||
this.userID,
|
||||
this.height,
|
||||
this.shouldShowActions, {
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -84,7 +82,7 @@ class FadingAppBarState extends State<FadingAppBar> {
|
|||
),
|
||||
),
|
||||
),
|
||||
height: Platform.isAndroid ? 80 : 96,
|
||||
Size.fromHeight(Platform.isAndroid ? 80 : 96),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -114,7 +112,7 @@ class FadingAppBarState extends State<FadingAppBar> {
|
|||
bool isFileHidden = false;
|
||||
if (isOwnedByUser && isFileUploaded) {
|
||||
isFileHidden = CollectionsService.instance
|
||||
.getCollectionByID(widget.file.collectionID)
|
||||
.getCollectionByID(widget.file.collectionID!)
|
||||
?.isHidden() ??
|
||||
false;
|
||||
}
|
||||
|
@ -231,7 +229,7 @@ class FadingAppBarState extends State<FadingAppBar> {
|
|||
}
|
||||
return items;
|
||||
},
|
||||
onSelected: (value) {
|
||||
onSelected: (dynamic value) {
|
||||
if (value == 1) {
|
||||
_download(widget.file);
|
||||
} else if (value == 2) {
|
||||
|
@ -285,7 +283,7 @@ class FadingAppBarState extends State<FadingAppBar> {
|
|||
}
|
||||
|
||||
Widget _getFavoriteButton() {
|
||||
return FutureBuilder(
|
||||
return FutureBuilder<bool>(
|
||||
future: FavoritesService.instance.isFavorite(widget.file),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
|
@ -297,7 +295,7 @@ class FadingAppBarState extends State<FadingAppBar> {
|
|||
);
|
||||
}
|
||||
|
||||
Widget _getLikeButton(File file, bool isLiked) {
|
||||
Widget _getLikeButton(File file, bool? isLiked) {
|
||||
return LikeButton(
|
||||
isLiked: isLiked,
|
||||
onTap: (oldValue) async {
|
||||
|
@ -305,7 +303,7 @@ class FadingAppBarState extends State<FadingAppBar> {
|
|||
bool hasError = false;
|
||||
if (isLiked) {
|
||||
final shouldBlockUser = file.uploadedFileID == null;
|
||||
ProgressDialog dialog;
|
||||
late ProgressDialog dialog;
|
||||
if (shouldBlockUser) {
|
||||
dialog = createProgressDialog(context, "Adding to favorites...");
|
||||
await dialog.show();
|
||||
|
@ -417,27 +415,27 @@ class FadingAppBarState extends State<FadingAppBar> {
|
|||
final FileType type = file.fileType;
|
||||
final bool downloadLivePhotoOnDroid =
|
||||
type == FileType.livePhoto && Platform.isAndroid;
|
||||
AssetEntity savedAsset;
|
||||
final io.File fileToSave = await getFile(file);
|
||||
AssetEntity? savedAsset;
|
||||
final io.File? fileToSave = await getFile(file);
|
||||
if (type == FileType.image) {
|
||||
savedAsset = await PhotoManager.editor
|
||||
.saveImageWithPath(fileToSave.path, title: file.title);
|
||||
.saveImageWithPath(fileToSave!.path, title: file.title!);
|
||||
} else if (type == FileType.video) {
|
||||
savedAsset =
|
||||
await PhotoManager.editor.saveVideo(fileToSave, title: file.title);
|
||||
savedAsset = await PhotoManager.editor
|
||||
.saveVideo(fileToSave!, title: file.title!);
|
||||
} else if (type == FileType.livePhoto) {
|
||||
final io.File liveVideoFile =
|
||||
final io.File? liveVideoFile =
|
||||
await getFileFromServer(file, liveVideo: true);
|
||||
if (liveVideoFile == null) {
|
||||
throw AssertionError("Live video can not be null");
|
||||
}
|
||||
if (downloadLivePhotoOnDroid) {
|
||||
await _saveLivePhotoOnDroid(fileToSave, liveVideoFile, file);
|
||||
await _saveLivePhotoOnDroid(fileToSave!, liveVideoFile, file);
|
||||
} else {
|
||||
savedAsset = await PhotoManager.editor.darwin.saveLivePhoto(
|
||||
imageFile: fileToSave,
|
||||
imageFile: fileToSave!,
|
||||
videoFile: liveVideoFile,
|
||||
title: file.title,
|
||||
title: file.title!,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -479,8 +477,11 @@ class FadingAppBarState extends State<FadingAppBar> {
|
|||
File enteFile,
|
||||
) async {
|
||||
debugPrint("Downloading LivePhoto on Droid");
|
||||
AssetEntity savedAsset = await PhotoManager.editor
|
||||
.saveImageWithPath(image.path, title: enteFile.title);
|
||||
AssetEntity? savedAsset = await (PhotoManager.editor
|
||||
.saveImageWithPath(image.path, title: enteFile.title!));
|
||||
if (savedAsset == null) {
|
||||
throw Exception("Failed to save image of live photo");
|
||||
}
|
||||
IgnoredFile ignoreVideoFile = IgnoredFile(
|
||||
savedAsset.id,
|
||||
savedAsset.title ?? '',
|
||||
|
@ -488,12 +489,16 @@ class FadingAppBarState extends State<FadingAppBar> {
|
|||
"remoteDownload",
|
||||
);
|
||||
await IgnoredFilesService.instance.cacheAndInsert([ignoreVideoFile]);
|
||||
final videoTitle = file_path.basenameWithoutExtension(enteFile.title) +
|
||||
final videoTitle = file_path.basenameWithoutExtension(enteFile.title!) +
|
||||
file_path.extension(video.path);
|
||||
savedAsset = (await PhotoManager.editor.saveVideo(
|
||||
savedAsset = (await (PhotoManager.editor.saveVideo(
|
||||
video,
|
||||
title: videoTitle,
|
||||
));
|
||||
)));
|
||||
if (savedAsset == null) {
|
||||
throw Exception("Failed to save video of live photo");
|
||||
}
|
||||
|
||||
ignoreVideoFile = IgnoredFile(
|
||||
savedAsset.id,
|
||||
savedAsset.title ?? videoTitle,
|
||||
|
@ -507,7 +512,10 @@ class FadingAppBarState extends State<FadingAppBar> {
|
|||
final dialog = createProgressDialog(context, "Please wait...");
|
||||
await dialog.show();
|
||||
try {
|
||||
final io.File fileToSave = await getFile(file);
|
||||
final io.File? fileToSave = await (getFile(file));
|
||||
if (fileToSave == null) {
|
||||
throw Exception("Fail to get file for setAs operation");
|
||||
}
|
||||
final m = MediaExtension();
|
||||
final bool result = await m.setAs("file://${fileToSave.path}", "image/*");
|
||||
if (result == false) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
|
@ -30,7 +30,7 @@ class FadingBottomBar extends StatefulWidget {
|
|||
this.file,
|
||||
this.onEditRequested,
|
||||
this.showOnlyInfoButton, {
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -96,7 +96,7 @@ class FadingBottomBarState extends State<FadingBottomBar> {
|
|||
bool isFileHidden = false;
|
||||
if (isUploadedByUser) {
|
||||
isFileHidden = CollectionsService.instance
|
||||
.getCollectionByID(widget.file.collectionID)
|
||||
.getCollectionByID(widget.file.collectionID!)
|
||||
?.isHidden() ??
|
||||
false;
|
||||
}
|
||||
|
@ -203,7 +203,7 @@ class FadingBottomBarState extends State<FadingBottomBar> {
|
|||
12,
|
||||
),
|
||||
child: Text(
|
||||
widget.file.caption,
|
||||
widget.file.caption!,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 4,
|
||||
style: getEnteTextTheme(context)
|
||||
|
@ -269,7 +269,7 @@ class FadingBottomBarState extends State<FadingBottomBar> {
|
|||
),
|
||||
onPressed: () async {
|
||||
final trashedFile = <TrashFile>[];
|
||||
trashedFile.add(widget.file);
|
||||
trashedFile.add(widget.file as TrashFile);
|
||||
if (await deleteFromTrash(context, trashedFile) == true) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/ente_theme_data.dart';
|
||||
|
||||
class FileInfoCollectionWidget extends StatelessWidget {
|
||||
final String name;
|
||||
final Function onTap;
|
||||
const FileInfoCollectionWidget({this.name, this.onTap, Key key})
|
||||
final String? name;
|
||||
final Function? onTap;
|
||||
const FileInfoCollectionWidget({this.name, this.onTap, Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
onTap: onTap as void Function()?,
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(
|
||||
top: 10,
|
||||
|
@ -32,7 +32,7 @@ class FileInfoCollectionWidget extends StatelessWidget {
|
|||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: Text(
|
||||
name,
|
||||
name!,
|
||||
style: Theme.of(context).textTheme.subtitle2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
// @dart=2.9
|
||||
|
||||
import 'dart:ui';
|
||||
|
||||
import "package:exif/exif.dart";
|
||||
import "package:flutter/cupertino.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
import "package:photos/core/configuration.dart";
|
||||
import 'package:photos/db/files_db.dart';
|
||||
import "package:photos/ente_theme_data.dart";
|
||||
|
@ -30,7 +29,7 @@ class FileInfoWidget extends StatefulWidget {
|
|||
|
||||
const FileInfoWidget(
|
||||
this.file, {
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -38,7 +37,7 @@ class FileInfoWidget extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _FileInfoWidgetState extends State<FileInfoWidget> {
|
||||
Map<String, IfdTag> _exif;
|
||||
Map<String, IfdTag>? _exif;
|
||||
final Map<String, dynamic> _exifData = {
|
||||
"focalLength": null,
|
||||
"fNumber": null,
|
||||
|
@ -50,7 +49,7 @@ class _FileInfoWidgetState extends State<FileInfoWidget> {
|
|||
};
|
||||
|
||||
bool _isImage = false;
|
||||
int _currentUserID;
|
||||
int? _currentUserID;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -76,22 +75,21 @@ class _FileInfoWidgetState extends State<FileInfoWidget> {
|
|||
final fileIsBackedup = file.uploadedFileID == null ? false : true;
|
||||
final bool isFileOwner =
|
||||
file.ownerID == null || file.ownerID == _currentUserID;
|
||||
Future<Set<int>> allCollectionIDsOfFile;
|
||||
Future<Set<String>>
|
||||
allDeviceFoldersOfFile; //Typing this as Future<Set<T>> as it would be easier to implement showing multiple device folders for a file in the future
|
||||
late Future<Set<int>> allCollectionIDsOfFile;
|
||||
; //Typing this as Future<Set<T>> as it would be easier to implement showing multiple device folders for a file in the future
|
||||
Future<Set<String>> allDeviceFoldersOfFile =
|
||||
Future.sync(() => {file.deviceFolder ?? ''});
|
||||
if (fileIsBackedup) {
|
||||
allCollectionIDsOfFile = FilesDB.instance.getAllCollectionIDsOfFile(
|
||||
file.uploadedFileID,
|
||||
file.uploadedFileID!,
|
||||
);
|
||||
} else {
|
||||
allDeviceFoldersOfFile = Future.sync(() => {file.deviceFolder});
|
||||
}
|
||||
final dateTime = DateTime.fromMicrosecondsSinceEpoch(file.creationTime);
|
||||
final dateTime = DateTime.fromMicrosecondsSinceEpoch(file.creationTime!);
|
||||
final dateTimeForUpdationTime =
|
||||
DateTime.fromMicrosecondsSinceEpoch(file.updationTime);
|
||||
DateTime.fromMicrosecondsSinceEpoch(file.updationTime!);
|
||||
|
||||
if (_isImage && _exif != null) {
|
||||
_generateExifForDetails(_exif);
|
||||
_generateExifForDetails(_exif!);
|
||||
}
|
||||
final bool showExifListTile = _exifData["focalLength"] != null ||
|
||||
_exifData["fNumber"] != null ||
|
||||
|
@ -100,7 +98,7 @@ class _FileInfoWidgetState extends State<FileInfoWidget> {
|
|||
_exifData["ISO"] != null;
|
||||
final bool showDimension =
|
||||
_exifData["resolution"] != null && _exifData["megaPixels"] != null;
|
||||
final listTiles = <Widget>[
|
||||
final listTiles = <Widget?>[
|
||||
!widget.file.isUploaded ||
|
||||
(!isFileOwner && (widget.file.caption?.isEmpty ?? true))
|
||||
? const SizedBox.shrink()
|
||||
|
@ -108,7 +106,7 @@ class _FileInfoWidgetState extends State<FileInfoWidget> {
|
|||
padding: const EdgeInsets.only(top: 8, bottom: 4),
|
||||
child: isFileOwner
|
||||
? FileCaptionWidget(file: widget.file)
|
||||
: FileCaptionReadyOnly(caption: widget.file.caption),
|
||||
: FileCaptionReadyOnly(caption: widget.file.caption!),
|
||||
),
|
||||
ListTile(
|
||||
horizontalTitleGap: 2,
|
||||
|
@ -118,12 +116,12 @@ class _FileInfoWidgetState extends State<FileInfoWidget> {
|
|||
),
|
||||
title: Text(
|
||||
getFullDate(
|
||||
DateTime.fromMicrosecondsSinceEpoch(file.creationTime),
|
||||
DateTime.fromMicrosecondsSinceEpoch(file.creationTime!),
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
getTimeIn12hrFormat(dateTime) + " " + dateTime.timeZoneName,
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
style: Theme.of(context).textTheme.bodyText2!.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.defaultTextColor
|
||||
|
@ -232,7 +230,7 @@ class _FileInfoWidgetState extends State<FileInfoWidget> {
|
|||
title: fileIsBackedup
|
||||
? CollectionsListOfFileWidget(
|
||||
allCollectionIDsOfFile,
|
||||
_currentUserID,
|
||||
_currentUserID!,
|
||||
)
|
||||
: DeviceFoldersListOfFileWidget(allDeviceFoldersOfFile),
|
||||
),
|
||||
|
@ -246,14 +244,14 @@ class _FileInfoWidgetState extends State<FileInfoWidget> {
|
|||
),
|
||||
title: Text(
|
||||
getFullDate(
|
||||
DateTime.fromMicrosecondsSinceEpoch(file.updationTime),
|
||||
DateTime.fromMicrosecondsSinceEpoch(file.updationTime!),
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
getTimeIn12hrFormat(dateTimeForUpdationTime) +
|
||||
" " +
|
||||
dateTimeForUpdationTime.timeZoneName,
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
style: Theme.of(context).textTheme.bodyText2!.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.defaultTextColor
|
||||
|
@ -320,14 +318,14 @@ class _FileInfoWidgetState extends State<FileInfoWidget> {
|
|||
if (file.uploadedFileID == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
String addedBy;
|
||||
String? addedBy;
|
||||
if (file.ownerID == _currentUserID) {
|
||||
if (file.pubMagicMetadata.uploaderName != null) {
|
||||
addedBy = file.pubMagicMetadata.uploaderName;
|
||||
if (file.pubMagicMetadata!.uploaderName != null) {
|
||||
addedBy = file.pubMagicMetadata!.uploaderName;
|
||||
}
|
||||
} else {
|
||||
final fileOwner = CollectionsService.instance
|
||||
.getFileOwner(file.ownerID, file.collectionID);
|
||||
.getFileOwner(file.ownerID!, file.collectionID);
|
||||
if (fileOwner != null) {
|
||||
addedBy = fileOwner.email;
|
||||
}
|
||||
|
@ -349,15 +347,15 @@ class _FileInfoWidgetState extends State<FileInfoWidget> {
|
|||
_generateExifForDetails(Map<String, IfdTag> exif) {
|
||||
if (exif["EXIF FocalLength"] != null) {
|
||||
_exifData["focalLength"] =
|
||||
(exif["EXIF FocalLength"].values.toList()[0] as Ratio).numerator /
|
||||
(exif["EXIF FocalLength"].values.toList()[0] as Ratio)
|
||||
(exif["EXIF FocalLength"]!.values.toList()[0] as Ratio).numerator /
|
||||
(exif["EXIF FocalLength"]!.values.toList()[0] as Ratio)
|
||||
.denominator;
|
||||
}
|
||||
|
||||
if (exif["EXIF FNumber"] != null) {
|
||||
_exifData["fNumber"] =
|
||||
(exif["EXIF FNumber"].values.toList()[0] as Ratio).numerator /
|
||||
(exif["EXIF FNumber"].values.toList()[0] as Ratio).denominator;
|
||||
(exif["EXIF FNumber"]!.values.toList()[0] as Ratio).numerator /
|
||||
(exif["EXIF FNumber"]!.values.toList()[0] as Ratio).denominator;
|
||||
}
|
||||
final imageWidth = exif["EXIF ExifImageWidth"] ?? exif["Image ImageWidth"];
|
||||
final imageLength = exif["EXIF ExifImageLength"] ??
|
||||
|
@ -390,14 +388,14 @@ class _FileInfoWidgetState extends State<FileInfoWidget> {
|
|||
if (widget.file.fileSize != null) {
|
||||
fileSizeFuture = Future.value(widget.file.fileSize);
|
||||
} else {
|
||||
fileSizeFuture = getFile(widget.file).then((f) => f.length());
|
||||
fileSizeFuture = getFile(widget.file).then((f) => f!.length());
|
||||
}
|
||||
return FutureBuilder(
|
||||
return FutureBuilder<int>(
|
||||
future: fileSizeFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return Text(
|
||||
(snapshot.data / (1024 * 1024)).toStringAsFixed(2) + " MB",
|
||||
(snapshot.data! / (1024 * 1024)).toStringAsFixed(2) + " MB",
|
||||
);
|
||||
} else {
|
||||
return Center(
|
||||
|
@ -416,15 +414,15 @@ class _FileInfoWidgetState extends State<FileInfoWidget> {
|
|||
Widget _getVideoDuration() {
|
||||
if (widget.file.duration != 0) {
|
||||
return Text(
|
||||
secondsToHHMMSS(widget.file.duration),
|
||||
secondsToHHMMSS(widget.file.duration!),
|
||||
);
|
||||
}
|
||||
return FutureBuilder(
|
||||
return FutureBuilder<AssetEntity?>(
|
||||
future: widget.file.getAsset,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return Text(
|
||||
snapshot.data.videoDuration.toString().split(".")[0],
|
||||
snapshot.data!.videoDuration.toString().split(".")[0],
|
||||
);
|
||||
} else {
|
||||
return Center(
|
||||
|
@ -445,7 +443,7 @@ class _FileInfoWidgetState extends State<FileInfoWidget> {
|
|||
context,
|
||||
minTime: DateTime(1800, 1, 1),
|
||||
maxTime: DateTime.now(),
|
||||
currentTime: DateTime.fromMicrosecondsSinceEpoch(file.creationTime),
|
||||
currentTime: DateTime.fromMicrosecondsSinceEpoch(file.creationTime!),
|
||||
locale: LocaleType.en,
|
||||
theme: Theme.of(context).colorScheme.dateTimePickertheme,
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
@ -10,11 +10,11 @@ import 'package:photos/ui/viewer/file/zoomable_live_image.dart';
|
|||
|
||||
class FileWidget extends StatelessWidget {
|
||||
final File file;
|
||||
final String tagPrefix;
|
||||
final Function(bool) shouldDisableScroll;
|
||||
final Function(bool) playbackCallback;
|
||||
final BoxDecoration backgroundDecoration;
|
||||
final bool autoPlay;
|
||||
final String? tagPrefix;
|
||||
final Function(bool)? shouldDisableScroll;
|
||||
final Function(bool)? playbackCallback;
|
||||
final BoxDecoration? backgroundDecoration;
|
||||
final bool? autoPlay;
|
||||
|
||||
const FileWidget(
|
||||
this.file, {
|
||||
|
@ -23,7 +23,7 @@ class FileWidget extends StatelessWidget {
|
|||
this.playbackCallback,
|
||||
this.tagPrefix,
|
||||
this.backgroundDecoration,
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:exif/exif.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -15,8 +15,8 @@ enum Status {
|
|||
|
||||
class RawExifListTileWidget extends StatelessWidget {
|
||||
final File file;
|
||||
final Map<String, IfdTag> exif;
|
||||
const RawExifListTileWidget(this.exif, this.file, {Key key})
|
||||
final Map<String, IfdTag>? exif;
|
||||
const RawExifListTileWidget(this.exif, this.file, {Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -24,7 +24,7 @@ class RawExifListTileWidget extends StatelessWidget {
|
|||
Status exifStatus = Status.loading;
|
||||
if (exif == null) {
|
||||
exifStatus = Status.loading;
|
||||
} else if (exif.isNotEmpty) {
|
||||
} else if (exif!.isNotEmpty) {
|
||||
exifStatus = Status.exifIsAvailable;
|
||||
} else {
|
||||
exifStatus = Status.noExif;
|
||||
|
@ -58,7 +58,7 @@ class RawExifListTileWidget extends StatelessWidget {
|
|||
: exifStatus == Status.exifIsAvailable
|
||||
? "View all EXIF data"
|
||||
: "No EXIF data",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
style: Theme.of(context).textTheme.bodyText2!.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.defaultTextColor
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// @dart=2.9
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/cache/thumbnail_cache.dart';
|
||||
|
@ -23,20 +21,20 @@ import 'package:photos/utils/file_util.dart';
|
|||
import 'package:photos/utils/thumbnail_util.dart';
|
||||
|
||||
class ThumbnailWidget extends StatefulWidget {
|
||||
final File file;
|
||||
final File? file;
|
||||
final BoxFit fit;
|
||||
final bool shouldShowSyncStatus;
|
||||
final bool shouldShowArchiveStatus;
|
||||
final bool showFavForAlbumOnly;
|
||||
final bool shouldShowLivePhotoOverlay;
|
||||
final Duration diskLoadDeferDuration;
|
||||
final Duration serverLoadDeferDuration;
|
||||
final Duration? diskLoadDeferDuration;
|
||||
final Duration? serverLoadDeferDuration;
|
||||
final int thumbnailSize;
|
||||
final bool shouldShowOwnerAvatar;
|
||||
|
||||
ThumbnailWidget(
|
||||
this.file, {
|
||||
Key key,
|
||||
Key? key,
|
||||
this.fit = BoxFit.cover,
|
||||
this.shouldShowSyncStatus = true,
|
||||
this.shouldShowLivePhotoOverlay = false,
|
||||
|
@ -46,7 +44,7 @@ class ThumbnailWidget extends StatefulWidget {
|
|||
this.diskLoadDeferDuration,
|
||||
this.serverLoadDeferDuration,
|
||||
this.thumbnailSize = thumbnailSmallSize,
|
||||
}) : super(key: key ?? Key(file.tag));
|
||||
}) : super(key: key ?? Key(file!.tag));
|
||||
|
||||
@override
|
||||
State<ThumbnailWidget> createState() => _ThumbnailWidgetState();
|
||||
|
@ -59,7 +57,7 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
|
|||
bool _errorLoadingLocalThumbnail = false;
|
||||
bool _isLoadingRemoteThumbnail = false;
|
||||
bool _errorLoadingRemoteThumbnail = false;
|
||||
ImageProvider _imageProvider;
|
||||
ImageProvider? _imageProvider;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -71,8 +69,8 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
|
|||
super.dispose();
|
||||
Future.delayed(const Duration(milliseconds: 10), () {
|
||||
// Cancel request only if the widget has been unmounted
|
||||
if (!mounted && widget.file.isRemoteFile && !_hasLoadedThumbnail) {
|
||||
removePendingGetThumbnailRequestIfAny(widget.file);
|
||||
if (!mounted && widget.file!.isRemoteFile && !_hasLoadedThumbnail) {
|
||||
removePendingGetThumbnailRequestIfAny(widget.file!);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -80,62 +78,62 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
|
|||
@override
|
||||
void didUpdateWidget(ThumbnailWidget oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (widget.file.generatedID != oldWidget.file.generatedID) {
|
||||
if (widget.file!.generatedID != oldWidget.file!.generatedID) {
|
||||
_reset();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.file.isRemoteFile) {
|
||||
if (widget.file!.isRemoteFile) {
|
||||
_loadNetworkImage();
|
||||
} else {
|
||||
_loadLocalImage(context);
|
||||
}
|
||||
Widget image;
|
||||
Widget? image;
|
||||
if (_imageProvider != null) {
|
||||
image = Image(
|
||||
image: _imageProvider,
|
||||
image: _imageProvider!,
|
||||
fit: widget.fit,
|
||||
);
|
||||
}
|
||||
// todo: [2ndJuly22] pref-review if the content Widget which depends on
|
||||
// thumbnail fetch logic should be part of separate stateFull widget.
|
||||
// If yes, parent thumbnail widget can be stateless
|
||||
Widget content;
|
||||
Widget? content;
|
||||
if (image != null) {
|
||||
final List<Widget> contentChildren = [image];
|
||||
if (FavoritesService.instance.isFavoriteCache(
|
||||
widget.file,
|
||||
widget.file!,
|
||||
checkOnlyAlbum: widget.showFavForAlbumOnly,
|
||||
)) {
|
||||
contentChildren.add(const FavoriteOverlayIcon());
|
||||
}
|
||||
if (widget.file.fileType == FileType.video) {
|
||||
if (widget.file!.fileType == FileType.video) {
|
||||
contentChildren.add(const VideoOverlayIcon());
|
||||
} else if (widget.file.fileType == FileType.livePhoto &&
|
||||
} else if (widget.file!.fileType == FileType.livePhoto &&
|
||||
widget.shouldShowLivePhotoOverlay) {
|
||||
contentChildren.add(const LivePhotoOverlayIcon());
|
||||
}
|
||||
if (widget.shouldShowOwnerAvatar) {
|
||||
final owner = CollectionsService.instance
|
||||
.getFileOwner(widget.file.ownerID, widget.file.collectionID);
|
||||
if (widget.file.ownerID != null &&
|
||||
widget.file.ownerID != Configuration.instance.getUserID()) {
|
||||
if (widget.file!.ownerID != null &&
|
||||
widget.file!.ownerID != Configuration.instance.getUserID()) {
|
||||
final owner = CollectionsService.instance
|
||||
.getFileOwner(widget.file!.ownerID!, widget.file!.collectionID);
|
||||
// hide this icon if the current thumbnail is being showed as album
|
||||
// cover
|
||||
contentChildren.add(
|
||||
OwnerAvatarOverlayIcon(owner),
|
||||
);
|
||||
} else if (widget.file.pubMagicMetadata.uploaderName != null) {
|
||||
} else if (widget.file!.pubMagicMetadata!.uploaderName != null) {
|
||||
contentChildren.add(
|
||||
// Use uploadName hashCode as userID so that different uploader
|
||||
// get avatar color
|
||||
OwnerAvatarOverlayIcon(
|
||||
User(
|
||||
id: widget.file.pubMagicMetadata.uploaderName.sumAsciiValues,
|
||||
email: owner.email,
|
||||
name: widget.file.pubMagicMetadata.uploaderName,
|
||||
id: widget.file!.pubMagicMetadata!.uploaderName.sumAsciiValues,
|
||||
email: '',
|
||||
name: widget.file!.pubMagicMetadata!.uploaderName,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -156,11 +154,11 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
|
|||
child: content,
|
||||
)
|
||||
];
|
||||
if (widget.shouldShowSyncStatus && widget.file.uploadedFileID == null) {
|
||||
if (widget.shouldShowSyncStatus && widget.file!.uploadedFileID == null) {
|
||||
viewChildren.add(const UnSyncedIcon());
|
||||
}
|
||||
if (widget.file is TrashFile) {
|
||||
viewChildren.add(TrashedFileOverlayText(widget.file));
|
||||
viewChildren.add(TrashedFileOverlayText(widget.file as TrashFile));
|
||||
}
|
||||
// todo: Move this icon overlay to the collection widget.
|
||||
if (widget.shouldShowArchiveStatus) {
|
||||
|
@ -179,13 +177,13 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
|
|||
!_isLoadingLocalThumbnail) {
|
||||
_isLoadingLocalThumbnail = true;
|
||||
final cachedSmallThumbnail =
|
||||
ThumbnailLruCache.get(widget.file, thumbnailSmallSize);
|
||||
ThumbnailLruCache.get(widget.file!, thumbnailSmallSize);
|
||||
if (cachedSmallThumbnail != null) {
|
||||
_imageProvider = Image.memory(cachedSmallThumbnail).image;
|
||||
_hasLoadedThumbnail = true;
|
||||
} else {
|
||||
if (widget.diskLoadDeferDuration != null) {
|
||||
Future.delayed(widget.diskLoadDeferDuration, () {
|
||||
Future.delayed(widget.diskLoadDeferDuration!, () {
|
||||
if (mounted) {
|
||||
_getThumbnailFromDisk();
|
||||
}
|
||||
|
@ -199,23 +197,23 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
|
|||
|
||||
Future _getThumbnailFromDisk() async {
|
||||
getThumbnailFromLocal(
|
||||
widget.file,
|
||||
widget.file!,
|
||||
size: widget.thumbnailSize,
|
||||
).then((thumbData) async {
|
||||
if (thumbData == null) {
|
||||
if (widget.file.uploadedFileID != null) {
|
||||
_logger.fine("Removing localID reference for " + widget.file.tag);
|
||||
widget.file.localID = null;
|
||||
if (widget.file!.uploadedFileID != null) {
|
||||
_logger.fine("Removing localID reference for " + widget.file!.tag);
|
||||
widget.file!.localID = null;
|
||||
if (widget.file is TrashFile) {
|
||||
TrashDB.instance.update(widget.file);
|
||||
TrashDB.instance.update(widget.file as TrashFile);
|
||||
} else {
|
||||
FilesDB.instance.update(widget.file);
|
||||
FilesDB.instance.update(widget.file!);
|
||||
}
|
||||
_loadNetworkImage();
|
||||
} else {
|
||||
if (await doesLocalFileExist(widget.file) == false) {
|
||||
_logger.info("Deleting file " + widget.file.tag);
|
||||
FilesDB.instance.deleteLocalFile(widget.file);
|
||||
if (await doesLocalFileExist(widget.file!) == false) {
|
||||
_logger.info("Deleting file " + widget.file!.tag);
|
||||
FilesDB.instance.deleteLocalFile(widget.file!);
|
||||
Bus.instance.fire(
|
||||
LocalPhotosUpdatedEvent(
|
||||
[widget.file],
|
||||
|
@ -232,7 +230,7 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
|
|||
final imageProvider = Image.memory(thumbData).image;
|
||||
_cacheAndRender(imageProvider);
|
||||
}
|
||||
ThumbnailLruCache.put(widget.file, thumbData, thumbnailSmallSize);
|
||||
ThumbnailLruCache.put(widget.file!, thumbData, thumbnailSmallSize);
|
||||
}).catchError((e) {
|
||||
_logger.warning("Could not load image: ", e);
|
||||
_errorLoadingLocalThumbnail = true;
|
||||
|
@ -244,14 +242,14 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
|
|||
!_errorLoadingRemoteThumbnail &&
|
||||
!_isLoadingRemoteThumbnail) {
|
||||
_isLoadingRemoteThumbnail = true;
|
||||
final cachedThumbnail = ThumbnailLruCache.get(widget.file);
|
||||
final cachedThumbnail = ThumbnailLruCache.get(widget.file!);
|
||||
if (cachedThumbnail != null) {
|
||||
_imageProvider = Image.memory(cachedThumbnail).image;
|
||||
_hasLoadedThumbnail = true;
|
||||
return;
|
||||
}
|
||||
if (widget.serverLoadDeferDuration != null) {
|
||||
Future.delayed(widget.serverLoadDeferDuration, () {
|
||||
Future.delayed(widget.serverLoadDeferDuration!, () {
|
||||
if (mounted) {
|
||||
_getThumbnailFromServer();
|
||||
}
|
||||
|
@ -264,7 +262,7 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
|
|||
|
||||
void _getThumbnailFromServer() async {
|
||||
try {
|
||||
final thumbnail = await getThumbnailFromServer(widget.file);
|
||||
final thumbnail = await getThumbnailFromServer(widget.file!);
|
||||
if (mounted) {
|
||||
final imageProvider = Image.memory(thumbnail).image;
|
||||
_cacheAndRender(imageProvider);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
|
@ -10,7 +10,7 @@ import 'package:photos/utils/date_time_util.dart';
|
|||
import 'package:video_player/video_player.dart';
|
||||
|
||||
class VideoControls extends StatefulWidget {
|
||||
const VideoControls({Key key}) : super(key: key);
|
||||
const VideoControls({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
|
@ -19,27 +19,27 @@ class VideoControls extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _VideoControlsState extends State<VideoControls> {
|
||||
VideoPlayerValue _latestValue;
|
||||
VideoPlayerValue? _latestValue;
|
||||
bool _hideStuff = true;
|
||||
Timer _hideTimer;
|
||||
Timer _initTimer;
|
||||
Timer _showAfterExpandCollapseTimer;
|
||||
Timer? _hideTimer;
|
||||
Timer? _initTimer;
|
||||
Timer? _showAfterExpandCollapseTimer;
|
||||
bool _dragging = false;
|
||||
bool _displayTapped = false;
|
||||
|
||||
final barHeight = 120.0;
|
||||
final marginSize = 5.0;
|
||||
|
||||
VideoPlayerController controller;
|
||||
ChewieController chewieController;
|
||||
late VideoPlayerController controller;
|
||||
ChewieController? chewieController;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_latestValue.hasError) {
|
||||
return chewieController.errorBuilder != null
|
||||
? chewieController.errorBuilder(
|
||||
if (_latestValue!.hasError) {
|
||||
return chewieController!.errorBuilder != null
|
||||
? chewieController!.errorBuilder!(
|
||||
context,
|
||||
chewieController.videoPlayerController.value.errorDescription,
|
||||
chewieController!.videoPlayerController.value.errorDescription!,
|
||||
)
|
||||
: Center(
|
||||
child: Icon(
|
||||
|
@ -63,9 +63,9 @@ class _VideoControlsState extends State<VideoControls> {
|
|||
Column(
|
||||
children: [
|
||||
_latestValue != null &&
|
||||
!_latestValue.isPlaying &&
|
||||
_latestValue.duration == null ||
|
||||
_latestValue.isBuffering
|
||||
!_latestValue!.isPlaying &&
|
||||
_latestValue!.duration == null ||
|
||||
_latestValue!.isBuffering
|
||||
? const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
)
|
||||
|
@ -100,7 +100,7 @@ class _VideoControlsState extends State<VideoControls> {
|
|||
void didChangeDependencies() {
|
||||
final oldController = chewieController;
|
||||
chewieController = ChewieController.of(context);
|
||||
controller = chewieController.videoPlayerController;
|
||||
controller = chewieController!.videoPlayerController;
|
||||
|
||||
if (oldController != chewieController) {
|
||||
_dispose();
|
||||
|
@ -113,7 +113,7 @@ class _VideoControlsState extends State<VideoControls> {
|
|||
Widget _buildBottomBar(
|
||||
BuildContext context,
|
||||
) {
|
||||
final iconColor = Theme.of(context).textTheme.button.color;
|
||||
final iconColor = Theme.of(context).textTheme.button!.color;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(bottom: 60),
|
||||
|
@ -126,7 +126,7 @@ class _VideoControlsState extends State<VideoControls> {
|
|||
child: Row(
|
||||
children: <Widget>[
|
||||
_buildCurrentPosition(iconColor),
|
||||
chewieController.isLive ? const SizedBox() : _buildProgressBar(),
|
||||
chewieController!.isLive ? const SizedBox() : _buildProgressBar(),
|
||||
_buildTotalDuration(iconColor),
|
||||
],
|
||||
),
|
||||
|
@ -167,7 +167,7 @@ class _VideoControlsState extends State<VideoControls> {
|
|||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Icon(
|
||||
_latestValue.isPlaying ? Icons.pause : Icons.play_arrow,
|
||||
_latestValue!.isPlaying ? Icons.pause : Icons.play_arrow,
|
||||
color: Colors.white, // same for both themes
|
||||
size: 64.0,
|
||||
),
|
||||
|
@ -180,9 +180,9 @@ class _VideoControlsState extends State<VideoControls> {
|
|||
);
|
||||
}
|
||||
|
||||
Widget _buildCurrentPosition(Color iconColor) {
|
||||
final position = _latestValue != null && _latestValue.position != null
|
||||
? _latestValue.position
|
||||
Widget _buildCurrentPosition(Color? iconColor) {
|
||||
final position = _latestValue != null && _latestValue!.position != null
|
||||
? _latestValue!.position
|
||||
: Duration.zero;
|
||||
|
||||
return Container(
|
||||
|
@ -197,9 +197,9 @@ class _VideoControlsState extends State<VideoControls> {
|
|||
);
|
||||
}
|
||||
|
||||
Widget _buildTotalDuration(Color iconColor) {
|
||||
final duration = _latestValue != null && _latestValue.duration != null
|
||||
? _latestValue.duration
|
||||
Widget _buildTotalDuration(Color? iconColor) {
|
||||
final duration = _latestValue != null && _latestValue!.duration != null
|
||||
? _latestValue!.duration
|
||||
: Duration.zero;
|
||||
|
||||
return Padding(
|
||||
|
@ -230,11 +230,11 @@ class _VideoControlsState extends State<VideoControls> {
|
|||
_updateState();
|
||||
|
||||
if ((controller.value != null && controller.value.isPlaying) ||
|
||||
chewieController.autoPlay) {
|
||||
chewieController!.autoPlay) {
|
||||
_startHideTimer();
|
||||
}
|
||||
|
||||
if (chewieController.showControlsOnInitialize) {
|
||||
if (chewieController!.showControlsOnInitialize) {
|
||||
_initTimer = Timer(const Duration(milliseconds: 200), () {
|
||||
setState(() {
|
||||
_hideStuff = false;
|
||||
|
@ -244,7 +244,7 @@ class _VideoControlsState extends State<VideoControls> {
|
|||
}
|
||||
|
||||
void _playPause() {
|
||||
final bool isFinished = _latestValue.position >= _latestValue.duration;
|
||||
final bool isFinished = _latestValue!.position >= _latestValue!.duration;
|
||||
|
||||
setState(() {
|
||||
if (controller.value.isPlaying) {
|
||||
|
@ -302,7 +302,7 @@ class _VideoControlsState extends State<VideoControls> {
|
|||
|
||||
_startHideTimer();
|
||||
},
|
||||
colors: chewieController.materialProgressColors ??
|
||||
colors: chewieController!.materialProgressColors ??
|
||||
ChewieProgressColors(
|
||||
playedColor: Theme.of(context).colorScheme.greenAlternative,
|
||||
handleColor: Colors.white,
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// @dart=2.9
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io' as io;
|
||||
|
||||
|
@ -21,16 +19,16 @@ import 'package:wakelock/wakelock.dart';
|
|||
|
||||
class VideoWidget extends StatefulWidget {
|
||||
final File file;
|
||||
final bool autoPlay;
|
||||
final String tagPrefix;
|
||||
final Function(bool) playbackCallback;
|
||||
final bool? autoPlay;
|
||||
final String? tagPrefix;
|
||||
final Function(bool)? playbackCallback;
|
||||
|
||||
const VideoWidget(
|
||||
this.file, {
|
||||
this.autoPlay = false,
|
||||
this.tagPrefix,
|
||||
this.playbackCallback,
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -39,10 +37,10 @@ class VideoWidget extends StatefulWidget {
|
|||
|
||||
class _VideoWidgetState extends State<VideoWidget> {
|
||||
final _logger = Logger("VideoWidget");
|
||||
VideoPlayerController _videoPlayerController;
|
||||
ChewieController _chewieController;
|
||||
double _progress;
|
||||
bool _isPlaying;
|
||||
VideoPlayerController? _videoPlayerController;
|
||||
ChewieController? _chewieController;
|
||||
double? _progress;
|
||||
bool _isPlaying = false;
|
||||
bool _wakeLockEnabledHere = false;
|
||||
|
||||
@override
|
||||
|
@ -78,7 +76,7 @@ class _VideoWidgetState extends State<VideoWidget> {
|
|||
if (widget.file.fileSize == null &&
|
||||
widget.file.ownerID == Configuration.instance.getUserID()) {
|
||||
FilesService.instance
|
||||
.getFileSize(widget.file.uploadedFileID)
|
||||
.getFileSize(widget.file.uploadedFileID!)
|
||||
.then((value) {
|
||||
widget.file.fileSize = value;
|
||||
if (mounted) {
|
||||
|
@ -111,10 +109,10 @@ class _VideoWidgetState extends State<VideoWidget> {
|
|||
@override
|
||||
void dispose() {
|
||||
if (_videoPlayerController != null) {
|
||||
_videoPlayerController.dispose();
|
||||
_videoPlayerController!.dispose();
|
||||
}
|
||||
if (_chewieController != null) {
|
||||
_chewieController.dispose();
|
||||
_chewieController!.dispose();
|
||||
}
|
||||
if (_wakeLockEnabledHere) {
|
||||
unawaited(
|
||||
|
@ -126,12 +124,13 @@ class _VideoWidgetState extends State<VideoWidget> {
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
VideoPlayerController _setVideoPlayerController({String url, io.File file}) {
|
||||
VideoPlayerController _setVideoPlayerController(
|
||||
{String? url, io.File? file}) {
|
||||
VideoPlayerController videoPlayerController;
|
||||
if (url != null) {
|
||||
videoPlayerController = VideoPlayerController.network(url);
|
||||
} else {
|
||||
videoPlayerController = VideoPlayerController.file(file);
|
||||
videoPlayerController = VideoPlayerController.file(file!);
|
||||
}
|
||||
return _videoPlayerController = videoPlayerController
|
||||
..initialize().whenComplete(() {
|
||||
|
@ -144,7 +143,7 @@ class _VideoWidgetState extends State<VideoWidget> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final content = _videoPlayerController != null &&
|
||||
_videoPlayerController.value.isInitialized
|
||||
_videoPlayerController!.value.isInitialized
|
||||
? _getVideoPlayer()
|
||||
: _getLoadingWidget();
|
||||
final contentWithDetector = GestureDetector(
|
||||
|
@ -158,12 +157,12 @@ class _VideoWidgetState extends State<VideoWidget> {
|
|||
onVisibilityChanged: (info) {
|
||||
if (info.visibleFraction < 1) {
|
||||
if (mounted && _chewieController != null) {
|
||||
_chewieController.pause();
|
||||
_chewieController!.pause();
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Hero(
|
||||
tag: widget.tagPrefix + widget.file.tag,
|
||||
tag: widget.tagPrefix! + widget.file.tag,
|
||||
child: contentWithDetector,
|
||||
),
|
||||
);
|
||||
|
@ -225,19 +224,19 @@ class _VideoWidgetState extends State<VideoWidget> {
|
|||
}
|
||||
|
||||
Widget _getVideoPlayer() {
|
||||
_videoPlayerController.addListener(() {
|
||||
if (_isPlaying != _videoPlayerController.value.isPlaying) {
|
||||
_isPlaying = _videoPlayerController.value.isPlaying;
|
||||
_videoPlayerController!.addListener(() {
|
||||
if (_isPlaying != _videoPlayerController!.value.isPlaying) {
|
||||
_isPlaying = _videoPlayerController!.value.isPlaying;
|
||||
if (widget.playbackCallback != null) {
|
||||
widget.playbackCallback(_isPlaying);
|
||||
widget.playbackCallback!(_isPlaying);
|
||||
}
|
||||
unawaited(_keepScreenAliveOnPlaying(_isPlaying));
|
||||
unawaited(_keepScreenAliveOnPlaying(_isPlaying!));
|
||||
}
|
||||
});
|
||||
_chewieController = ChewieController(
|
||||
videoPlayerController: _videoPlayerController,
|
||||
aspectRatio: _videoPlayerController.value.aspectRatio,
|
||||
autoPlay: widget.autoPlay,
|
||||
videoPlayerController: _videoPlayerController!,
|
||||
aspectRatio: _videoPlayerController!.value.aspectRatio,
|
||||
autoPlay: widget.autoPlay!,
|
||||
autoInitialize: true,
|
||||
looping: true,
|
||||
allowMuting: true,
|
||||
|
@ -246,7 +245,7 @@ class _VideoWidgetState extends State<VideoWidget> {
|
|||
);
|
||||
return Container(
|
||||
color: Colors.black,
|
||||
child: Chewie(controller: _chewieController),
|
||||
child: Chewie(controller: _chewieController!),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
|
@ -18,16 +18,16 @@ import 'package:photos/utils/file_util.dart';
|
|||
import 'package:photos/utils/thumbnail_util.dart';
|
||||
|
||||
class ZoomableImage extends StatefulWidget {
|
||||
final File photo;
|
||||
final Function(bool) shouldDisableScroll;
|
||||
final String tagPrefix;
|
||||
final Decoration backgroundDecoration;
|
||||
final File? photo;
|
||||
final Function(bool)? shouldDisableScroll;
|
||||
final String? tagPrefix;
|
||||
final Decoration? backgroundDecoration;
|
||||
|
||||
const ZoomableImage(
|
||||
this.photo, {
|
||||
Key key,
|
||||
Key? key,
|
||||
this.shouldDisableScroll,
|
||||
@required this.tagPrefix,
|
||||
required this.tagPrefix,
|
||||
this.backgroundDecoration,
|
||||
}) : super(key: key);
|
||||
|
||||
|
@ -38,14 +38,14 @@ class ZoomableImage extends StatefulWidget {
|
|||
class _ZoomableImageState extends State<ZoomableImage>
|
||||
with SingleTickerProviderStateMixin {
|
||||
final Logger _logger = Logger("ZoomableImage");
|
||||
File _photo;
|
||||
ImageProvider _imageProvider;
|
||||
File? _photo;
|
||||
ImageProvider? _imageProvider;
|
||||
bool _loadedSmallThumbnail = false;
|
||||
bool _loadingLargeThumbnail = false;
|
||||
bool _loadedLargeThumbnail = false;
|
||||
bool _loadingFinalImage = false;
|
||||
bool _loadedFinalImage = false;
|
||||
ValueChanged<PhotoViewScaleState> _scaleStateChangedCallback;
|
||||
ValueChanged<PhotoViewScaleState>? _scaleStateChangedCallback;
|
||||
bool _isZooming = false;
|
||||
|
||||
@override
|
||||
|
@ -54,7 +54,7 @@ class _ZoomableImageState extends State<ZoomableImage>
|
|||
debugPrint('initState for ${_photo.toString()}');
|
||||
_scaleStateChangedCallback = (value) {
|
||||
if (widget.shouldDisableScroll != null) {
|
||||
widget.shouldDisableScroll(value != PhotoViewScaleState.initial);
|
||||
widget.shouldDisableScroll!(value != PhotoViewScaleState.initial);
|
||||
}
|
||||
_isZooming = value != PhotoViewScaleState.initial;
|
||||
debugPrint("isZooming = $_isZooming, currentState $value");
|
||||
|
@ -65,7 +65,7 @@ class _ZoomableImageState extends State<ZoomableImage>
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_photo.isRemoteFile) {
|
||||
if (_photo!.isRemoteFile) {
|
||||
_loadNetworkImage();
|
||||
} else {
|
||||
_loadLocalImage(context);
|
||||
|
@ -81,16 +81,16 @@ class _ZoomableImageState extends State<ZoomableImage>
|
|||
minScale: PhotoViewComputedScale.contained,
|
||||
gaplessPlayback: true,
|
||||
heroAttributes: PhotoViewHeroAttributes(
|
||||
tag: widget.tagPrefix + _photo.tag,
|
||||
tag: widget.tagPrefix! + _photo!.tag,
|
||||
),
|
||||
backgroundDecoration: widget.backgroundDecoration,
|
||||
backgroundDecoration: widget.backgroundDecoration as BoxDecoration?,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
content = const EnteLoadingWidget();
|
||||
}
|
||||
|
||||
final GestureDragUpdateCallback verticalDragCallback = _isZooming
|
||||
final GestureDragUpdateCallback? verticalDragCallback = _isZooming
|
||||
? null
|
||||
: (d) => {
|
||||
if (!_isZooming && d.delta.dy > dragSensitivity)
|
||||
|
@ -104,12 +104,12 @@ class _ZoomableImageState extends State<ZoomableImage>
|
|||
|
||||
void _loadNetworkImage() {
|
||||
if (!_loadedSmallThumbnail && !_loadedFinalImage) {
|
||||
final cachedThumbnail = ThumbnailLruCache.get(_photo);
|
||||
final cachedThumbnail = ThumbnailLruCache.get(_photo!);
|
||||
if (cachedThumbnail != null) {
|
||||
_imageProvider = Image.memory(cachedThumbnail).image;
|
||||
_loadedSmallThumbnail = true;
|
||||
} else {
|
||||
getThumbnailFromServer(_photo).then((file) {
|
||||
getThumbnailFromServer(_photo!).then((file) {
|
||||
final imageProvider = Image.memory(file).image;
|
||||
if (mounted) {
|
||||
precacheImage(imageProvider, context).then((value) {
|
||||
|
@ -128,10 +128,10 @@ class _ZoomableImageState extends State<ZoomableImage>
|
|||
}
|
||||
}
|
||||
if (!_loadedFinalImage) {
|
||||
getFileFromServer(_photo).then((file) {
|
||||
getFileFromServer(_photo!).then((file) {
|
||||
_onFinalImageLoaded(
|
||||
Image.file(
|
||||
file,
|
||||
file!,
|
||||
gaplessPlayback: true,
|
||||
).image,
|
||||
);
|
||||
|
@ -143,7 +143,7 @@ class _ZoomableImageState extends State<ZoomableImage>
|
|||
if (!_loadedSmallThumbnail &&
|
||||
!_loadedLargeThumbnail &&
|
||||
!_loadedFinalImage) {
|
||||
final cachedThumbnail = ThumbnailLruCache.get(_photo, thumbnailSmallSize);
|
||||
final cachedThumbnail = ThumbnailLruCache.get(_photo!, thumbnailSmallSize);
|
||||
if (cachedThumbnail != null) {
|
||||
_imageProvider = Image.memory(cachedThumbnail).image;
|
||||
_loadedSmallThumbnail = true;
|
||||
|
@ -154,7 +154,7 @@ class _ZoomableImageState extends State<ZoomableImage>
|
|||
!_loadedLargeThumbnail &&
|
||||
!_loadedFinalImage) {
|
||||
_loadingLargeThumbnail = true;
|
||||
getThumbnailFromLocal(_photo, size: thumbnailLargeSize, quality: 100)
|
||||
getThumbnailFromLocal(_photo!, size: thumbnailLargeSize, quality: 100)
|
||||
.then((cachedThumbnail) {
|
||||
if (cachedThumbnail != null) {
|
||||
_onLargeThumbnailLoaded(Image.memory(cachedThumbnail).image, context);
|
||||
|
@ -165,7 +165,7 @@ class _ZoomableImageState extends State<ZoomableImage>
|
|||
if (!_loadingFinalImage && !_loadedFinalImage) {
|
||||
_loadingFinalImage = true;
|
||||
getFile(
|
||||
_photo,
|
||||
_photo!,
|
||||
isOrigin: Platform.isIOS &&
|
||||
_isGIF(), // since on iOS GIFs playback only when origin-files are loaded
|
||||
).then((file) {
|
||||
|
@ -173,12 +173,12 @@ class _ZoomableImageState extends State<ZoomableImage>
|
|||
_onFinalImageLoaded(Image.file(file).image);
|
||||
} else {
|
||||
_logger.info("File was deleted " + _photo.toString());
|
||||
if (_photo.uploadedFileID != null) {
|
||||
_photo.localID = null;
|
||||
FilesDB.instance.update(_photo);
|
||||
if (_photo!.uploadedFileID != null) {
|
||||
_photo!.localID = null;
|
||||
FilesDB.instance.update(_photo!);
|
||||
_loadNetworkImage();
|
||||
} else {
|
||||
FilesDB.instance.deleteLocalFile(_photo);
|
||||
FilesDB.instance.deleteLocalFile(_photo!);
|
||||
Bus.instance.fire(
|
||||
LocalPhotosUpdatedEvent(
|
||||
[_photo],
|
||||
|
@ -221,5 +221,5 @@ class _ZoomableImageState extends State<ZoomableImage>
|
|||
}
|
||||
}
|
||||
|
||||
bool _isGIF() => _photo.displayName.toLowerCase().endsWith(".gif");
|
||||
bool _isGIF() => _photo!.displayName.toLowerCase().endsWith(".gif");
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:io' as io;
|
||||
|
||||
|
@ -15,15 +15,15 @@ import 'package:video_player/video_player.dart';
|
|||
|
||||
class ZoomableLiveImage extends StatefulWidget {
|
||||
final File file;
|
||||
final Function(bool) shouldDisableScroll;
|
||||
final String tagPrefix;
|
||||
final Decoration backgroundDecoration;
|
||||
final Function(bool)? shouldDisableScroll;
|
||||
final String? tagPrefix;
|
||||
final Decoration? backgroundDecoration;
|
||||
|
||||
const ZoomableLiveImage(
|
||||
this.file, {
|
||||
Key key,
|
||||
Key? key,
|
||||
this.shouldDisableScroll,
|
||||
@required this.tagPrefix,
|
||||
required this.tagPrefix,
|
||||
this.backgroundDecoration,
|
||||
}) : super(key: key);
|
||||
|
||||
|
@ -34,12 +34,12 @@ class ZoomableLiveImage extends StatefulWidget {
|
|||
class _ZoomableLiveImageState extends State<ZoomableLiveImage>
|
||||
with SingleTickerProviderStateMixin {
|
||||
final Logger _logger = Logger("ZoomableLiveImage");
|
||||
File _file;
|
||||
File? _file;
|
||||
bool _showVideo = false;
|
||||
bool _isLoadingVideoPlayer = false;
|
||||
|
||||
VideoPlayerController _videoPlayerController;
|
||||
ChewieController _chewieController;
|
||||
VideoPlayerController? _videoPlayerController;
|
||||
ChewieController? _chewieController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -51,7 +51,7 @@ class _ZoomableLiveImageState extends State<ZoomableLiveImage>
|
|||
void _onLongPressEvent(bool isPressed) {
|
||||
if (_videoPlayerController != null && isPressed == false) {
|
||||
// stop playing video
|
||||
_videoPlayerController.pause();
|
||||
_videoPlayerController!.pause();
|
||||
}
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
|
@ -88,20 +88,20 @@ class _ZoomableLiveImageState extends State<ZoomableLiveImage>
|
|||
@override
|
||||
void dispose() {
|
||||
if (_videoPlayerController != null) {
|
||||
_videoPlayerController.pause();
|
||||
_videoPlayerController.dispose();
|
||||
_videoPlayerController!.pause();
|
||||
_videoPlayerController!.dispose();
|
||||
}
|
||||
if (_chewieController != null) {
|
||||
_chewieController.dispose();
|
||||
_chewieController!.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Widget _getVideoPlayer() {
|
||||
_videoPlayerController.seekTo(Duration.zero);
|
||||
_videoPlayerController!.seekTo(Duration.zero);
|
||||
_chewieController = ChewieController(
|
||||
videoPlayerController: _videoPlayerController,
|
||||
aspectRatio: _videoPlayerController.value.aspectRatio,
|
||||
videoPlayerController: _videoPlayerController!,
|
||||
aspectRatio: _videoPlayerController!.value.aspectRatio,
|
||||
autoPlay: true,
|
||||
autoInitialize: true,
|
||||
looping: true,
|
||||
|
@ -110,7 +110,7 @@ class _ZoomableLiveImageState extends State<ZoomableLiveImage>
|
|||
);
|
||||
return Container(
|
||||
color: Colors.black,
|
||||
child: Chewie(controller: _chewieController), // same for both theme
|
||||
child: Chewie(controller: _chewieController!), // same for both theme
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -120,14 +120,14 @@ class _ZoomableLiveImageState extends State<ZoomableLiveImage>
|
|||
return;
|
||||
}
|
||||
_isLoadingVideoPlayer = true;
|
||||
if (_file.isRemoteFile && !(await isFileCached(_file, liveVideo: true))) {
|
||||
if (_file!.isRemoteFile && !(await isFileCached(_file!, liveVideo: true))) {
|
||||
showShortToast(context, "Downloading...");
|
||||
}
|
||||
|
||||
var videoFile = await getFile(widget.file, liveVideo: true)
|
||||
io.File? videoFile = await getFile(widget.file, liveVideo: true)
|
||||
.timeout(const Duration(seconds: 15))
|
||||
.onError((e, s) {
|
||||
_logger.info("getFile failed ${_file.tag}", e);
|
||||
.onError((dynamic e, s) {
|
||||
_logger.info("getFile failed ${_file!.tag}", e);
|
||||
return null;
|
||||
});
|
||||
|
||||
|
@ -135,11 +135,11 @@ class _ZoomableLiveImageState extends State<ZoomableLiveImage>
|
|||
// getFile with liveVideo as true can fail for file with localID when
|
||||
// the live photo was downloaded from remote.
|
||||
if ((videoFile == null || !videoFile.existsSync()) &&
|
||||
_file.uploadedFileID != null) {
|
||||
_file!.uploadedFileID != null) {
|
||||
videoFile = await getFileFromServer(widget.file, liveVideo: true)
|
||||
.timeout(const Duration(seconds: 15))
|
||||
.onError((e, s) {
|
||||
_logger.info("getRemoteFile failed ${_file.tag}", e);
|
||||
.onError((dynamic e, s) {
|
||||
_logger.info("getRemoteFile failed ${_file!.tag}", e);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ class _ZoomableLiveImageState extends State<ZoomableLiveImage>
|
|||
_isLoadingVideoPlayer = false;
|
||||
}
|
||||
|
||||
VideoPlayerController _setVideoPlayerController({io.File file}) {
|
||||
VideoPlayerController _setVideoPlayerController({required io.File file}) {
|
||||
final videoPlayerController = VideoPlayerController.file(file);
|
||||
return _videoPlayerController = videoPlayerController
|
||||
..initialize().whenComplete(() {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:collection/collection.dart' show IterableExtension;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
|
@ -23,7 +24,7 @@ class ArchivePage extends StatelessWidget {
|
|||
this.tagPrefix = "archived_page",
|
||||
this.appBarType = GalleryType.archive,
|
||||
this.overlayType = GalleryType.archive,
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -35,7 +36,7 @@ class ArchivePage extends StatelessWidget {
|
|||
return FilesDB.instance.getAllPendingOrUploadedFiles(
|
||||
creationStartTime,
|
||||
creationEndTime,
|
||||
Configuration.instance.getUserID(),
|
||||
Configuration.instance.getUserID()!,
|
||||
visibility: visibilityArchive,
|
||||
limit: limit,
|
||||
asc: asc,
|
||||
|
@ -44,9 +45,8 @@ class ArchivePage extends StatelessWidget {
|
|||
},
|
||||
reloadEvent: Bus.instance.on<FilesUpdatedEvent>().where(
|
||||
(event) =>
|
||||
event.updatedFiles.firstWhere(
|
||||
event.updatedFiles.firstWhereOrNull(
|
||||
(element) => element.uploadedFileID != null,
|
||||
orElse: () => null,
|
||||
) !=
|
||||
null,
|
||||
),
|
||||
|
@ -54,9 +54,8 @@ class ArchivePage extends StatelessWidget {
|
|||
forceReloadEvents: [
|
||||
Bus.instance.on<FilesUpdatedEvent>().where(
|
||||
(event) =>
|
||||
event.updatedFiles.firstWhere(
|
||||
event.updatedFiles.firstWhereOrNull(
|
||||
(element) => element.uploadedFileID != null,
|
||||
orElse: () => null,
|
||||
) !=
|
||||
null,
|
||||
),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
|
@ -26,7 +26,7 @@ class CollectionPage extends StatefulWidget {
|
|||
this.tagPrefix = "collection",
|
||||
this.appBarType = GalleryType.ownedCollection,
|
||||
this.hasVerifiedLock = false,
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
|
@ -20,7 +20,7 @@ class DeviceFolderPage extends StatelessWidget {
|
|||
final DeviceCollection deviceCollection;
|
||||
final _selectedFiles = SelectedFiles();
|
||||
|
||||
DeviceFolderPage(this.deviceCollection, {Key key}) : super(key: key);
|
||||
DeviceFolderPage(this.deviceCollection, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(Object context) {
|
||||
|
@ -74,7 +74,7 @@ class DeviceFolderPage extends StatelessWidget {
|
|||
class BackupConfigurationHeaderWidget extends StatefulWidget {
|
||||
final DeviceCollection deviceCollection;
|
||||
|
||||
const BackupConfigurationHeaderWidget(this.deviceCollection, {Key key})
|
||||
const BackupConfigurationHeaderWidget(this.deviceCollection, {Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -84,7 +84,7 @@ class BackupConfigurationHeaderWidget extends StatefulWidget {
|
|||
|
||||
class _BackupConfigurationHeaderWidgetState
|
||||
extends State<BackupConfigurationHeaderWidget> {
|
||||
bool _isBackedUp;
|
||||
late bool _isBackedUp;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// @dart=2.9
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
@ -25,28 +23,28 @@ import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
|||
typedef GalleryLoader = Future<FileLoadResult> Function(
|
||||
int creationStartTime,
|
||||
int creationEndTime, {
|
||||
int limit,
|
||||
bool asc,
|
||||
int? limit,
|
||||
bool? asc,
|
||||
});
|
||||
|
||||
class Gallery extends StatefulWidget {
|
||||
final GalleryLoader asyncLoader;
|
||||
final List<File> initialFiles;
|
||||
final Stream<FilesUpdatedEvent> reloadEvent;
|
||||
final List<Stream<Event>> forceReloadEvents;
|
||||
final List<File?>? initialFiles;
|
||||
final Stream<FilesUpdatedEvent>? reloadEvent;
|
||||
final List<Stream<Event>>? forceReloadEvents;
|
||||
final Set<EventType> removalEventTypes;
|
||||
final SelectedFiles selectedFiles;
|
||||
final SelectedFiles? selectedFiles;
|
||||
final String tagPrefix;
|
||||
final Widget header;
|
||||
final Widget footer;
|
||||
final Widget? header;
|
||||
final Widget? footer;
|
||||
final Widget emptyState;
|
||||
final String albumName;
|
||||
final String? albumName;
|
||||
final double scrollBottomSafeArea;
|
||||
|
||||
const Gallery({
|
||||
@required this.asyncLoader,
|
||||
@required this.selectedFiles,
|
||||
@required this.tagPrefix,
|
||||
required this.asyncLoader,
|
||||
required this.selectedFiles,
|
||||
required this.tagPrefix,
|
||||
this.initialFiles,
|
||||
this.reloadEvent,
|
||||
this.forceReloadEvents,
|
||||
|
@ -56,7 +54,7 @@ class Gallery extends StatefulWidget {
|
|||
this.emptyState = const EmptyState(),
|
||||
this.scrollBottomSafeArea = 120.0,
|
||||
this.albumName = '',
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -70,25 +68,25 @@ class _GalleryState extends State<Gallery> {
|
|||
|
||||
final _hugeListViewKey = GlobalKey<HugeListViewState>();
|
||||
|
||||
Logger _logger;
|
||||
List<List<File>> _collatedFiles = [];
|
||||
late Logger _logger;
|
||||
List<List<File?>> _collatedFiles = [];
|
||||
bool _hasLoadedFiles = false;
|
||||
ItemScrollController _itemScroller;
|
||||
StreamSubscription<FilesUpdatedEvent> _reloadEventSubscription;
|
||||
StreamSubscription<TabDoubleTapEvent> _tabDoubleTapEvent;
|
||||
ItemScrollController? _itemScroller;
|
||||
StreamSubscription<FilesUpdatedEvent>? _reloadEventSubscription;
|
||||
StreamSubscription<TabDoubleTapEvent>? _tabDoubleTapEvent;
|
||||
final _forceReloadEventSubscriptions = <StreamSubscription<Event>>[];
|
||||
String _logTag;
|
||||
int _photoGridSize;
|
||||
String? _logTag;
|
||||
int? _photoGridSize;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_logTag =
|
||||
"Gallery_${widget.tagPrefix}${kDebugMode ? "_" + widget.albumName : ""}";
|
||||
_logger = Logger(_logTag);
|
||||
"Gallery_${widget.tagPrefix}${kDebugMode ? "_" + widget.albumName! : ""}";
|
||||
_logger = Logger(_logTag!);
|
||||
_logger.finest("init Gallery");
|
||||
_itemScroller = ItemScrollController();
|
||||
if (widget.reloadEvent != null) {
|
||||
_reloadEventSubscription = widget.reloadEvent.listen((event) async {
|
||||
_reloadEventSubscription = widget.reloadEvent!.listen((event) async {
|
||||
// In soft refresh, setState is called for entire gallery only when
|
||||
// number of child change
|
||||
_logger.finest("Soft refresh all files on ${event.reason} ");
|
||||
|
@ -106,14 +104,14 @@ class _GalleryState extends State<Gallery> {
|
|||
// todo: Assign ID to Gallery and fire generic event with ID &
|
||||
// target index/date
|
||||
if (mounted && event.selectedIndex == 0) {
|
||||
_itemScroller.scrollTo(
|
||||
_itemScroller!.scrollTo(
|
||||
index: 0,
|
||||
duration: const Duration(milliseconds: 150),
|
||||
);
|
||||
}
|
||||
});
|
||||
if (widget.forceReloadEvents != null) {
|
||||
for (final event in widget.forceReloadEvents) {
|
||||
for (final event in widget.forceReloadEvents!) {
|
||||
_forceReloadEventSubscriptions.add(
|
||||
event.listen((event) async {
|
||||
_logger.finest("Force refresh all files on ${event.reason}");
|
||||
|
@ -124,7 +122,7 @@ class _GalleryState extends State<Gallery> {
|
|||
}
|
||||
}
|
||||
if (widget.initialFiles != null) {
|
||||
_onFilesLoaded(widget.initialFiles);
|
||||
_onFilesLoaded(widget.initialFiles!);
|
||||
}
|
||||
_loadFiles(limit: kInitialLoadLimit).then((result) async {
|
||||
_setFilesAndReload(result.files);
|
||||
|
@ -143,7 +141,7 @@ class _GalleryState extends State<Gallery> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<FileLoadResult> _loadFiles({int limit}) async {
|
||||
Future<FileLoadResult> _loadFiles({int? limit}) async {
|
||||
_logger.info("Loading ${limit ?? "all"} files");
|
||||
try {
|
||||
final startTime = DateTime.now().microsecondsSinceEpoch;
|
||||
|
@ -169,7 +167,7 @@ class _GalleryState extends State<Gallery> {
|
|||
}
|
||||
|
||||
// Collates files and returns `true` if it resulted in a gallery reload
|
||||
bool _onFilesLoaded(List<File> files) {
|
||||
bool _onFilesLoaded(List<File?> files) {
|
||||
final updatedCollatedFiles = _collateFiles(files);
|
||||
if (_collatedFiles.length != updatedCollatedFiles.length ||
|
||||
_collatedFiles.isEmpty) {
|
||||
|
@ -219,7 +217,7 @@ class _GalleryState extends State<Gallery> {
|
|||
emptyResultBuilder: (_) {
|
||||
final List<Widget> children = [];
|
||||
if (widget.header != null) {
|
||||
children.add(widget.header);
|
||||
children.add(widget.header!);
|
||||
}
|
||||
children.add(
|
||||
Expanded(
|
||||
|
@ -227,7 +225,7 @@ class _GalleryState extends State<Gallery> {
|
|||
),
|
||||
);
|
||||
if (widget.footer != null) {
|
||||
children.add(widget.footer);
|
||||
children.add(widget.footer!);
|
||||
}
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
|
@ -252,17 +250,17 @@ class _GalleryState extends State<Gallery> {
|
|||
photoGirdSize: _photoGridSize,
|
||||
);
|
||||
if (widget.header != null && index == 0) {
|
||||
gallery = Column(children: [widget.header, gallery]);
|
||||
gallery = Column(children: [widget.header!, gallery]);
|
||||
}
|
||||
if (widget.footer != null && index == _collatedFiles.length - 1) {
|
||||
gallery = Column(children: [gallery, widget.footer]);
|
||||
gallery = Column(children: [gallery, widget.footer!]);
|
||||
}
|
||||
return gallery;
|
||||
},
|
||||
labelTextBuilder: (int index) {
|
||||
return getMonthAndYear(
|
||||
DateTime.fromMicrosecondsSinceEpoch(
|
||||
_collatedFiles[index][0].creationTime,
|
||||
_collatedFiles[index][0]!.creationTime!,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -280,16 +278,16 @@ class _GalleryState extends State<Gallery> {
|
|||
);
|
||||
}
|
||||
|
||||
List<List<File>> _collateFiles(List<File> files) {
|
||||
final List<File> dailyFiles = [];
|
||||
final List<List<File>> collatedFiles = [];
|
||||
List<List<File?>> _collateFiles(List<File?> files) {
|
||||
final List<File?> dailyFiles = [];
|
||||
final List<List<File?>> collatedFiles = [];
|
||||
for (int index = 0; index < files.length; index++) {
|
||||
if (index > 0 &&
|
||||
!areFromSameDay(
|
||||
files[index - 1].creationTime,
|
||||
files[index].creationTime,
|
||||
files[index - 1]!.creationTime!,
|
||||
files[index]!.creationTime!,
|
||||
)) {
|
||||
final List<File> collatedDailyFiles = [];
|
||||
final List<File?> collatedDailyFiles = [];
|
||||
collatedDailyFiles.addAll(dailyFiles);
|
||||
collatedFiles.add(collatedDailyFiles);
|
||||
dailyFiles.clear();
|
||||
|
@ -300,7 +298,7 @@ class _GalleryState extends State<Gallery> {
|
|||
collatedFiles.add(dailyFiles);
|
||||
}
|
||||
collatedFiles
|
||||
.sort((a, b) => b[0].creationTime.compareTo(a[0].creationTime));
|
||||
.sort((a, b) => b[0]!.creationTime!.compareTo(a[0]!.creationTime!));
|
||||
return collatedFiles;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
@ -35,16 +35,16 @@ import 'package:url_launcher/url_launcher_string.dart';
|
|||
|
||||
class GalleryAppBarWidget extends StatefulWidget {
|
||||
final GalleryType type;
|
||||
final String title;
|
||||
final String? title;
|
||||
final SelectedFiles selectedFiles;
|
||||
final DeviceCollection deviceCollection;
|
||||
final Collection collection;
|
||||
final DeviceCollection? deviceCollection;
|
||||
final Collection? collection;
|
||||
|
||||
const GalleryAppBarWidget(
|
||||
this.type,
|
||||
this.title,
|
||||
this.selectedFiles, {
|
||||
Key key,
|
||||
Key? key,
|
||||
this.deviceCollection,
|
||||
this.collection,
|
||||
}) : super(key: key);
|
||||
|
@ -55,9 +55,9 @@ class GalleryAppBarWidget extends StatefulWidget {
|
|||
|
||||
class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
||||
final _logger = Logger("GalleryAppBar");
|
||||
StreamSubscription _userAuthEventSubscription;
|
||||
Function() _selectedFilesListener;
|
||||
String _appBarTitle;
|
||||
late StreamSubscription _userAuthEventSubscription;
|
||||
late Function() _selectedFilesListener;
|
||||
String? _appBarTitle;
|
||||
final GlobalKey shareButtonKey = GlobalKey();
|
||||
|
||||
@override
|
||||
|
@ -95,10 +95,10 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
? const SizedBox.shrink()
|
||||
: TextButton(
|
||||
child: Text(
|
||||
_appBarTitle,
|
||||
_appBarTitle!,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.headline5
|
||||
.headline5!
|
||||
.copyWith(fontSize: 16),
|
||||
),
|
||||
onPressed: () => _renameAlbum(context),
|
||||
|
@ -119,14 +119,14 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
barrierColor: Colors.black.withOpacity(0.85),
|
||||
);
|
||||
// indicates user cancelled the rename request
|
||||
if (result == null || result.trim() == _appBarTitle.trim()) {
|
||||
if (result == null || result.trim() == _appBarTitle!.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final dialog = createProgressDialog(context, "Changing name...");
|
||||
await dialog.show();
|
||||
try {
|
||||
await CollectionsService.instance.rename(widget.collection, result);
|
||||
await CollectionsService.instance.rename(widget.collection!, result);
|
||||
await dialog.hide();
|
||||
if (mounted) {
|
||||
_appBarTitle = result;
|
||||
|
@ -139,7 +139,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
}
|
||||
|
||||
Future<dynamic> _leaveAlbum(BuildContext context) async {
|
||||
final DialogUserChoice result = await showChoiceDialog(
|
||||
final DialogUserChoice? result = await showChoiceDialog(
|
||||
context,
|
||||
"Leave shared album?",
|
||||
"You will leave the album, and it will stop being visible to you.",
|
||||
|
@ -154,7 +154,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
final dialog = createProgressDialog(context, "Leaving album...");
|
||||
await dialog.show();
|
||||
try {
|
||||
await CollectionsService.instance.leaveAlbum(widget.collection);
|
||||
await CollectionsService.instance.leaveAlbum(widget.collection!);
|
||||
await dialog.hide();
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
|
@ -175,7 +175,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
BackupStatus status;
|
||||
try {
|
||||
status = await SyncService.instance
|
||||
.getBackupStatus(pathID: widget.deviceCollection.id);
|
||||
.getBackupStatus(pathID: widget.deviceCollection!.id);
|
||||
} catch (e) {
|
||||
await dialog.hide();
|
||||
showGenericErrorDialog(context: context);
|
||||
|
@ -190,7 +190,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
"You've no files in this album that can be deleted",
|
||||
);
|
||||
} else {
|
||||
final bool result = await routeToPage(
|
||||
final bool? result = await routeToPage(
|
||||
context,
|
||||
FreeSpacePage(status, clearSpaceForFolder: true),
|
||||
);
|
||||
|
@ -254,7 +254,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
}
|
||||
final List<PopupMenuItem> items = [];
|
||||
if (widget.type == GalleryType.ownedCollection) {
|
||||
if (widget.collection.type != CollectionType.favorites) {
|
||||
if (widget.collection!.type != CollectionType.favorites) {
|
||||
items.add(
|
||||
PopupMenuItem(
|
||||
value: 1,
|
||||
|
@ -270,10 +270,10 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
),
|
||||
);
|
||||
}
|
||||
final bool isArchived = widget.collection.isArchived();
|
||||
final bool isArchived = widget.collection!.isArchived();
|
||||
// Do not show archive option for favorite collection. If collection is
|
||||
// already archived, allow user to unarchive that collection.
|
||||
if (isArchived || widget.collection.type != CollectionType.favorites) {
|
||||
if (isArchived || widget.collection!.type != CollectionType.favorites) {
|
||||
items.add(
|
||||
PopupMenuItem(
|
||||
value: 2,
|
||||
|
@ -289,7 +289,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
),
|
||||
);
|
||||
}
|
||||
if (widget.collection.type != CollectionType.favorites) {
|
||||
if (widget.collection!.type != CollectionType.favorites) {
|
||||
items.add(
|
||||
PopupMenuItem(
|
||||
value: 3,
|
||||
|
@ -345,14 +345,14 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
itemBuilder: (context) {
|
||||
return items;
|
||||
},
|
||||
onSelected: (value) async {
|
||||
onSelected: (dynamic value) async {
|
||||
if (value == 1) {
|
||||
await _renameAlbum(context);
|
||||
} else if (value == 2) {
|
||||
await changeCollectionVisibility(
|
||||
context,
|
||||
widget.collection,
|
||||
widget.collection.isArchived()
|
||||
widget.collection!,
|
||||
widget.collection!.isArchived()
|
||||
? visibilityVisible
|
||||
: visibilityArchive,
|
||||
);
|
||||
|
@ -378,7 +378,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
await CollectionsService.instance.getCollectionsWithThumbnails();
|
||||
final bool isEmptyCollection = collectionWithThumbnail
|
||||
.firstWhereOrNull(
|
||||
(element) => element.collection.id == widget.collection.id,
|
||||
(element) => element.collection.id == widget.collection!.id,
|
||||
)
|
||||
?.thumbnail ==
|
||||
null;
|
||||
|
@ -403,7 +403,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
await dialog.show();
|
||||
try {
|
||||
await CollectionsService.instance
|
||||
.trashCollection(widget.collection, isEmptyCollection);
|
||||
.trashCollection(widget.collection!, isEmptyCollection);
|
||||
showShortToast(context, "Successfully deleted album");
|
||||
await dialog.hide();
|
||||
Navigator.of(context).pop();
|
||||
|
@ -424,7 +424,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
"Cannot share empty collection of typex ${widget.type}",
|
||||
);
|
||||
}
|
||||
if (Configuration.instance.getUserID() == widget.collection.owner.id) {
|
||||
if (Configuration.instance.getUserID() == widget.collection!.owner!.id) {
|
||||
unawaited(
|
||||
routeToPage(
|
||||
context,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
@ -28,15 +28,15 @@ import 'package:photos/utils/toast_util.dart';
|
|||
class GalleryOverlayWidget extends StatefulWidget {
|
||||
final GalleryType type;
|
||||
final SelectedFiles selectedFiles;
|
||||
final String path;
|
||||
final Collection collection;
|
||||
final String? path;
|
||||
final Collection? collection;
|
||||
|
||||
const GalleryOverlayWidget(
|
||||
this.type,
|
||||
this.selectedFiles, {
|
||||
this.path,
|
||||
this.collection,
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -44,8 +44,8 @@ class GalleryOverlayWidget extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _GalleryOverlayWidgetState extends State<GalleryOverlayWidget> {
|
||||
StreamSubscription _userAuthEventSubscription;
|
||||
Function() _selectedFilesListener;
|
||||
late StreamSubscription _userAuthEventSubscription;
|
||||
late Function() _selectedFilesListener;
|
||||
final GlobalKey shareButtonKey = GlobalKey();
|
||||
|
||||
@override
|
||||
|
@ -100,15 +100,15 @@ class _GalleryOverlayWidgetState extends State<GalleryOverlayWidget> {
|
|||
class OverlayWidget extends StatefulWidget {
|
||||
final GalleryType type;
|
||||
final SelectedFiles selectedFiles;
|
||||
final String path;
|
||||
final Collection collection;
|
||||
final String? path;
|
||||
final Collection? collection;
|
||||
|
||||
const OverlayWidget(
|
||||
this.type,
|
||||
this.selectedFiles, {
|
||||
this.path,
|
||||
this.collection,
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -117,8 +117,8 @@ class OverlayWidget extends StatefulWidget {
|
|||
|
||||
class _OverlayWidgetState extends State<OverlayWidget> {
|
||||
final _logger = Logger("GalleryOverlay");
|
||||
StreamSubscription _userAuthEventSubscription;
|
||||
Function() _selectedFilesListener;
|
||||
late StreamSubscription _userAuthEventSubscription;
|
||||
late Function() _selectedFilesListener;
|
||||
final GlobalKey shareButtonKey = GlobalKey();
|
||||
|
||||
@override
|
||||
|
@ -172,7 +172,7 @@ class _OverlayWidgetState extends State<OverlayWidget> {
|
|||
' selected',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.subtitle2
|
||||
.subtitle2!
|
||||
.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color:
|
||||
|
@ -210,7 +210,7 @@ class _OverlayWidgetState extends State<OverlayWidget> {
|
|||
child: Center(
|
||||
child: Text(
|
||||
'Cancel',
|
||||
style: Theme.of(context).textTheme.subtitle2.copyWith(
|
||||
style: Theme.of(context).textTheme.subtitle2!.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.iconColor,
|
||||
),
|
||||
|
@ -307,7 +307,7 @@ class _OverlayWidgetState extends State<OverlayWidget> {
|
|||
}
|
||||
if (Configuration.instance.hasConfiguredAccount() &&
|
||||
widget.type == GalleryType.ownedCollection &&
|
||||
widget.collection.type != CollectionType.favorites) {
|
||||
widget.collection!.type != CollectionType.favorites) {
|
||||
actions.add(
|
||||
Tooltip(
|
||||
message: "Move",
|
||||
|
@ -357,7 +357,7 @@ class _OverlayWidgetState extends State<OverlayWidget> {
|
|||
),
|
||||
);
|
||||
} else if (widget.type == GalleryType.ownedCollection) {
|
||||
if (widget.collection.type == CollectionType.folder) {
|
||||
if (widget.collection!.type == CollectionType.folder) {
|
||||
actions.add(
|
||||
Tooltip(
|
||||
message: "Delete",
|
||||
|
@ -645,7 +645,7 @@ class _OverlayWidgetState extends State<OverlayWidget> {
|
|||
" file" +
|
||||
(count == 1 ? "" : "s") +
|
||||
" from " +
|
||||
widget.collection.name +
|
||||
widget.collection!.name! +
|
||||
"?",
|
||||
),
|
||||
actions: <Widget>[
|
||||
|
@ -657,7 +657,7 @@ class _OverlayWidgetState extends State<OverlayWidget> {
|
|||
await dialog.show();
|
||||
try {
|
||||
await CollectionsService.instance.removeFromCollection(
|
||||
widget.collection.id,
|
||||
widget.collection!.id,
|
||||
widget.selectedFiles.files.toList(),
|
||||
);
|
||||
await dialog.hide();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:collection/collection.dart' show IterableExtension;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
|
@ -23,7 +24,7 @@ class HiddenPage extends StatelessWidget {
|
|||
this.tagPrefix = "hidden_page",
|
||||
this.appBarType = GalleryType.hidden,
|
||||
this.overlayType = GalleryType.hidden,
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -34,16 +35,15 @@ class HiddenPage extends StatelessWidget {
|
|||
CollectionsService.instance.getHiddenCollections().toList(),
|
||||
creationStartTime,
|
||||
creationEndTime,
|
||||
Configuration.instance.getUserID(),
|
||||
Configuration.instance.getUserID()!,
|
||||
limit: limit,
|
||||
asc: asc,
|
||||
);
|
||||
},
|
||||
reloadEvent: Bus.instance.on<FilesUpdatedEvent>().where(
|
||||
(event) =>
|
||||
event.updatedFiles.firstWhere(
|
||||
event.updatedFiles.firstWhereOrNull(
|
||||
(element) => element.uploadedFileID != null,
|
||||
orElse: () => null,
|
||||
) !=
|
||||
null,
|
||||
),
|
||||
|
@ -55,9 +55,8 @@ class HiddenPage extends StatelessWidget {
|
|||
forceReloadEvents: [
|
||||
Bus.instance.on<FilesUpdatedEvent>().where(
|
||||
(event) =>
|
||||
event.updatedFiles.firstWhere(
|
||||
event.updatedFiles.firstWhereOrNull(
|
||||
(element) => element.uploadedFileID != null,
|
||||
orElse: () => null,
|
||||
) !=
|
||||
null,
|
||||
),
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:collection/collection.dart' show IterableExtension;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
import 'package:photos/db/trash_db.dart';
|
||||
|
@ -24,7 +25,7 @@ class TrashPage extends StatefulWidget {
|
|||
this.tagPrefix = "trash_page",
|
||||
this.appBarType = GalleryType.trash,
|
||||
this.overlayType = GalleryType.trash,
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -32,7 +33,7 @@ class TrashPage extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _TrashPageState extends State<TrashPage> {
|
||||
Function() _selectedFilesListener;
|
||||
late Function() _selectedFilesListener;
|
||||
@override
|
||||
void initState() {
|
||||
_selectedFilesListener = () {
|
||||
|
@ -63,9 +64,8 @@ class _TrashPageState extends State<TrashPage> {
|
|||
},
|
||||
reloadEvent: Bus.instance.on<FilesUpdatedEvent>().where(
|
||||
(event) =>
|
||||
event.updatedFiles.firstWhere(
|
||||
event.updatedFiles.firstWhereOrNull(
|
||||
(element) => element.uploadedFileID != null,
|
||||
orElse: () => null,
|
||||
) !=
|
||||
null,
|
||||
),
|
||||
|
@ -124,12 +124,12 @@ class _TrashPageState extends State<TrashPage> {
|
|||
return FutureBuilder<int>(
|
||||
future: TrashDB.instance.count(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData && snapshot.data > 0) {
|
||||
if (snapshot.hasData && snapshot.data! > 0) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Text(
|
||||
'Items show the number the days remaining before permanent deletion',
|
||||
style: Theme.of(context).textTheme.caption.copyWith(fontSize: 16),
|
||||
style: Theme.of(context).textTheme.caption!.copyWith(fontSize: 16),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
|
@ -141,7 +141,7 @@ class _TrashPageState extends State<TrashPage> {
|
|||
}
|
||||
|
||||
class BottomButtonsWidget extends StatelessWidget {
|
||||
const BottomButtonsWidget({Key key}) : super(key: key);
|
||||
const BottomButtonsWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -167,7 +167,7 @@ class BottomButtonsWidget extends StatelessWidget {
|
|||
),
|
||||
child: Text(
|
||||
'Delete All',
|
||||
style: Theme.of(context).textTheme.subtitle2.copyWith(
|
||||
style: Theme.of(context).textTheme.subtitle2!.copyWith(
|
||||
color: const Color.fromRGBO(255, 101, 101, 1),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/ente_theme_data.dart';
|
||||
|
@ -11,7 +11,7 @@ import 'package:photos/utils/navigation_util.dart';
|
|||
class FileSearchResultWidget extends StatelessWidget {
|
||||
final FileSearchResult matchedFile;
|
||||
|
||||
const FileSearchResultWidget(this.matchedFile, {Key key}) : super(key: key);
|
||||
const FileSearchResultWidget(this.matchedFile, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -44,7 +44,7 @@ class FileSearchResultWidget extends StatelessWidget {
|
|||
SizedBox(
|
||||
width: 220,
|
||||
child: Text(
|
||||
matchedFile.file.title,
|
||||
matchedFile.file.title!,
|
||||
style: const TextStyle(fontSize: 18),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
|
@ -22,7 +22,7 @@ class SearchResultPage extends StatelessWidget {
|
|||
|
||||
SearchResultPage(
|
||||
this.searchResult, {
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -33,8 +33,8 @@ class SearchResultPage extends StatelessWidget {
|
|||
final result = files
|
||||
.where(
|
||||
(file) =>
|
||||
file.creationTime >= creationStartTime &&
|
||||
file.creationTime <= creationEndTime,
|
||||
file.creationTime! >= creationStartTime &&
|
||||
file.creationTime! <= creationEndTime,
|
||||
)
|
||||
.toList();
|
||||
return Future.value(
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/ente_theme_data.dart';
|
||||
|
||||
class SearchSuffixIcon extends StatefulWidget {
|
||||
final bool shouldShowSpinner;
|
||||
const SearchSuffixIcon(this.shouldShowSpinner, {Key key}) : super(key: key);
|
||||
const SearchSuffixIcon(this.shouldShowSpinner, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<SearchSuffixIcon> createState() => _SearchSuffixIconState();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
@ -18,7 +18,7 @@ class SearchSuggestionsWidget extends StatelessWidget {
|
|||
|
||||
const SearchSuggestionsWidget(
|
||||
this.results, {
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// @dart=2.9
|
||||
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
|
@ -17,7 +17,7 @@ import 'package:photos/utils/debouncer.dart';
|
|||
import 'package:photos/utils/navigation_util.dart';
|
||||
|
||||
class SearchIconWidget extends StatefulWidget {
|
||||
const SearchIconWidget({Key key}) : super(key: key);
|
||||
const SearchIconWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<SearchIconWidget> createState() => _SearchIconWidgetState();
|
||||
|
@ -50,7 +50,7 @@ class _SearchIconWidgetState extends State<SearchIconWidget> {
|
|||
}
|
||||
|
||||
class SearchWidget extends StatefulWidget {
|
||||
const SearchWidget({Key key}) : super(key: key);
|
||||
const SearchWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<SearchWidget> createState() => _SearchWidgetState();
|
||||
|
@ -129,7 +129,7 @@ class _SearchWidgetState extends State<SearchWidget> {
|
|||
builder: (
|
||||
BuildContext context,
|
||||
bool isDebouncing,
|
||||
Widget child,
|
||||
Widget? child,
|
||||
) {
|
||||
return SearchSuffixIcon(
|
||||
isDebouncing,
|
||||
|
|
|
@ -62,7 +62,7 @@ Future<void> sendLogs(
|
|||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return LogFileViewer(SuperLogging.logFile);
|
||||
return LogFileViewer(SuperLogging.logFile!);
|
||||
},
|
||||
barrierColor: Colors.black87,
|
||||
barrierDismissible: false,
|
||||
|
|
Loading…
Add table
Reference in a new issue