Merge pull request #609 from ente-io/redesign-with-components
Redesign with components
This commit is contained in:
commit
1f6a08d71a
12 changed files with 591 additions and 172 deletions
|
@ -43,7 +43,7 @@ const TextStyle large = TextStyle(
|
|||
);
|
||||
const TextStyle body = TextStyle(
|
||||
fontSize: 16,
|
||||
height: 19.4 / 16.0,
|
||||
height: 20 / 16.0,
|
||||
fontWeight: _regularWeight,
|
||||
fontFamily: _fontFamily,
|
||||
);
|
||||
|
|
138
lib/ui/backup_settings_screen.dart
Normal file
138
lib/ui/backup_settings_screen.dart
Normal file
|
@ -0,0 +1,138 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/theme/ente_theme.dart';
|
||||
import 'package:photos/ui/components/captioned_text_widget.dart';
|
||||
import 'package:photos/ui/components/icon_button_widget.dart';
|
||||
import 'package:photos/ui/components/menu_item_widget.dart';
|
||||
import 'package:photos/ui/components/menu_section_description_widget.dart';
|
||||
import 'package:photos/ui/components/title_bar_title_widget.dart';
|
||||
import 'package:photos/ui/components/title_bar_widget.dart';
|
||||
import 'package:photos/ui/components/toggle_switch_widget.dart';
|
||||
|
||||
class BackupSettingsScreen extends StatelessWidget {
|
||||
const BackupSettingsScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = getEnteColorScheme(context);
|
||||
return Scaffold(
|
||||
body: CustomScrollView(
|
||||
primary: false,
|
||||
slivers: <Widget>[
|
||||
TitleBarWidget(
|
||||
flexibleSpaceTitle: const TitleBarTitleWidget(
|
||||
title: "Backup settings",
|
||||
),
|
||||
actionIcons: [
|
||||
IconButtonWidget(
|
||||
icon: Icons.close_outlined,
|
||||
isSecondary: true,
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: const CaptionedTextWidget(
|
||||
title: "Backup over mobile data",
|
||||
),
|
||||
menuItemColor: colorScheme.fillFaint,
|
||||
trailingSwitch: ToggleSwitchWidget(
|
||||
value: () {
|
||||
return Configuration.instance
|
||||
.shouldBackupOverMobileData();
|
||||
},
|
||||
onChanged: () async {
|
||||
await Configuration.instance
|
||||
.setBackupOverMobileData(
|
||||
!Configuration.instance
|
||||
.shouldBackupOverMobileData(),
|
||||
);
|
||||
},
|
||||
),
|
||||
borderRadius: 8,
|
||||
alignCaptionedTextToLeft: true,
|
||||
isBottomBorderRadiusRemoved: true,
|
||||
isGestureDetectorDisabled: true,
|
||||
),
|
||||
const SizedBox(height: 1),
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: const CaptionedTextWidget(
|
||||
title: "Backup videos",
|
||||
),
|
||||
menuItemColor: colorScheme.fillFaint,
|
||||
trailingSwitch: ToggleSwitchWidget(
|
||||
value: () =>
|
||||
Configuration.instance.shouldBackupVideos(),
|
||||
onChanged: () => Configuration.instance
|
||||
.setShouldBackupVideos(
|
||||
!Configuration.instance.shouldBackupVideos(),
|
||||
),
|
||||
),
|
||||
borderRadius: 8,
|
||||
alignCaptionedTextToLeft: true,
|
||||
isTopBorderRadiusRemoved: true,
|
||||
isGestureDetectorDisabled: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Platform.isIOS
|
||||
? Column(
|
||||
children: [
|
||||
MenuItemWidget(
|
||||
captionedTextWidget:
|
||||
const CaptionedTextWidget(
|
||||
title: "Disable auto lock",
|
||||
),
|
||||
menuItemColor: colorScheme.fillFaint,
|
||||
trailingSwitch: ToggleSwitchWidget(
|
||||
value: () => Configuration.instance
|
||||
.shouldKeepDeviceAwake(),
|
||||
onChanged: () {
|
||||
return Configuration.instance
|
||||
.setShouldKeepDeviceAwake(
|
||||
!Configuration.instance
|
||||
.shouldKeepDeviceAwake(),
|
||||
);
|
||||
},
|
||||
),
|
||||
borderRadius: 8,
|
||||
alignCaptionedTextToLeft: true,
|
||||
isGestureDetectorDisabled: true,
|
||||
),
|
||||
const MenuSectionDescriptionWidget(
|
||||
content:
|
||||
"Disable the device screen lock when ente is in the foreground and there is a backup in progress. This is normally not needed, but may help big uploads and initial imports of large libraries complete faster.",
|
||||
)
|
||||
],
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
childCount: 1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ import 'dart:ui';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
import 'package:photos/events/opened_settings_event.dart';
|
||||
import 'package:photos/ui/components/icon_button_widget.dart';
|
||||
import 'package:photos/ui/viewer/search/search_widget.dart';
|
||||
|
||||
class HomeHeaderWidget extends StatefulWidget {
|
||||
|
@ -23,16 +24,13 @@ class _HomeHeaderWidgetState extends State<HomeHeaderWidget> {
|
|||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
IconButton(
|
||||
visualDensity: const VisualDensity(horizontal: -2, vertical: -2),
|
||||
onPressed: () {
|
||||
IconButtonWidget(
|
||||
isPrimary: true,
|
||||
icon: Icons.menu_outlined,
|
||||
onTap: () {
|
||||
Scaffold.of(context).openDrawer();
|
||||
Bus.instance.fire(OpenedSettingsEvent());
|
||||
},
|
||||
splashColor: Colors.transparent,
|
||||
icon: const Icon(
|
||||
Icons.menu_outlined,
|
||||
),
|
||||
),
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 250),
|
||||
|
|
102
lib/ui/components/icon_button_widget.dart
Normal file
102
lib/ui/components/icon_button_widget.dart
Normal file
|
@ -0,0 +1,102 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/theme/colors.dart';
|
||||
import 'package:photos/theme/ente_theme.dart';
|
||||
|
||||
class IconButtonWidget extends StatefulWidget {
|
||||
final bool isPrimary;
|
||||
final bool isSecondary;
|
||||
final bool isRounded;
|
||||
final IconData icon;
|
||||
final bool disableGestureDetector;
|
||||
final VoidCallback? onTap;
|
||||
final Color? defaultColor;
|
||||
final Color? pressedColor;
|
||||
final Color? iconColor;
|
||||
const IconButtonWidget({
|
||||
required this.icon,
|
||||
this.isPrimary = false,
|
||||
this.isSecondary = false,
|
||||
this.isRounded = false,
|
||||
this.disableGestureDetector = false,
|
||||
this.onTap,
|
||||
this.defaultColor,
|
||||
this.pressedColor,
|
||||
this.iconColor,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<IconButtonWidget> createState() => _IconButtonWidgetState();
|
||||
}
|
||||
|
||||
class _IconButtonWidgetState extends State<IconButtonWidget> {
|
||||
Color? iconStateColor;
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
setState(() {
|
||||
iconStateColor = null;
|
||||
});
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!widget.isPrimary && !widget.isRounded && !widget.isSecondary) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
final colorTheme = getEnteColorScheme(context);
|
||||
iconStateColor ??
|
||||
(iconStateColor = widget.defaultColor ??
|
||||
(widget.isRounded ? colorTheme.fillFaint : null));
|
||||
return widget.disableGestureDetector
|
||||
? _iconButton(colorTheme)
|
||||
: GestureDetector(
|
||||
onTapDown: _onTapDown,
|
||||
onTapUp: _onTapUp,
|
||||
onTapCancel: _onTapCancel,
|
||||
onTap: widget.onTap,
|
||||
child: _iconButton(colorTheme),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _iconButton(EnteColorScheme colorTheme) {
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 20),
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: iconStateColor,
|
||||
),
|
||||
child: Icon(
|
||||
widget.icon,
|
||||
color: widget.iconColor ??
|
||||
(widget.isSecondary
|
||||
? colorTheme.strokeMuted
|
||||
: colorTheme.strokeBase),
|
||||
size: 24,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_onTapDown(details) {
|
||||
final colorTheme = getEnteColorScheme(context);
|
||||
setState(() {
|
||||
iconStateColor = widget.pressedColor ??
|
||||
(widget.isRounded ? colorTheme.fillMuted : colorTheme.fillFaint);
|
||||
});
|
||||
}
|
||||
|
||||
_onTapUp(details) {
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
setState(() {
|
||||
iconStateColor = null;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_onTapCancel() {
|
||||
setState(() {
|
||||
iconStateColor = null;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -19,6 +19,10 @@ class MenuItemWidget extends StatefulWidget {
|
|||
final double borderRadius;
|
||||
final Color? pressedColor;
|
||||
final ExpandableController? expandableController;
|
||||
final bool isBottomBorderRadiusRemoved;
|
||||
final bool isTopBorderRadiusRemoved;
|
||||
//disable gesture detector if not used
|
||||
final bool isGestureDetectorDisabled;
|
||||
const MenuItemWidget({
|
||||
required this.captionedTextWidget,
|
||||
this.isExpandable = false,
|
||||
|
@ -34,6 +38,9 @@ class MenuItemWidget extends StatefulWidget {
|
|||
this.borderRadius = 4.0,
|
||||
this.pressedColor,
|
||||
this.expandableController,
|
||||
this.isBottomBorderRadiusRemoved = false,
|
||||
this.isTopBorderRadiusRemoved = false,
|
||||
this.isGestureDetectorDisabled = false,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
|
@ -70,7 +77,7 @@ class _MenuItemWidgetState extends State<MenuItemWidget> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.isExpandable
|
||||
return widget.isExpandable || widget.isGestureDetectorDisabled
|
||||
? menuItemWidget(context)
|
||||
: GestureDetector(
|
||||
onTap: widget.onTap,
|
||||
|
@ -86,7 +93,11 @@ class _MenuItemWidgetState extends State<MenuItemWidget> {
|
|||
final enteColorScheme = Theme.of(context).colorScheme.enteTheme.colorScheme;
|
||||
final borderRadius = Radius.circular(widget.borderRadius);
|
||||
final isExpanded = widget.expandableController?.value;
|
||||
final bottomBorderRadius = isExpanded != null && isExpanded
|
||||
final bottomBorderRadius =
|
||||
(isExpanded != null && isExpanded) || widget.isBottomBorderRadiusRemoved
|
||||
? const Radius.circular(0)
|
||||
: borderRadius;
|
||||
final topBorderRadius = widget.isTopBorderRadiusRemoved
|
||||
? const Radius.circular(0)
|
||||
: borderRadius;
|
||||
return AnimatedContainer(
|
||||
|
@ -95,8 +106,8 @@ class _MenuItemWidgetState extends State<MenuItemWidget> {
|
|||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: borderRadius,
|
||||
topRight: borderRadius,
|
||||
topLeft: topBorderRadius,
|
||||
topRight: topBorderRadius,
|
||||
bottomLeft: bottomBorderRadius,
|
||||
bottomRight: bottomBorderRadius,
|
||||
),
|
||||
|
@ -155,7 +166,7 @@ class _MenuItemWidgetState extends State<MenuItemWidget> {
|
|||
|
||||
void _onTapDown(details) {
|
||||
setState(() {
|
||||
menuItemColor = widget.pressedColor;
|
||||
menuItemColor = widget.pressedColor ?? widget.menuItemColor;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
20
lib/ui/components/menu_section_description_widget.dart
Normal file
20
lib/ui/components/menu_section_description_widget.dart
Normal file
|
@ -0,0 +1,20 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/theme/ente_theme.dart';
|
||||
|
||||
class MenuSectionDescriptionWidget extends StatelessWidget {
|
||||
final String content;
|
||||
const MenuSectionDescriptionWidget({required this.content, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6),
|
||||
child: Text(
|
||||
content,
|
||||
style: getEnteTextTheme(context)
|
||||
.mini
|
||||
.copyWith(color: getEnteColorScheme(context).textMuted),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:photos/ente_theme_data.dart';
|
||||
import 'package:photos/theme/colors.dart';
|
||||
import 'package:photos/theme/text_style.dart';
|
||||
import 'package:photos/ui/components/icon_button_widget.dart';
|
||||
|
||||
class NotificationWarningWidget extends StatelessWidget {
|
||||
final IconData warningIcon;
|
||||
|
@ -51,23 +52,14 @@ class NotificationWarningWidget extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
ClipOval(
|
||||
child: Material(
|
||||
color: fillFaintDark,
|
||||
child: InkWell(
|
||||
splashColor: Colors.red, // Splash color
|
||||
onTap: onTap,
|
||||
child: SizedBox(
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: Icon(
|
||||
actionIcon,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButtonWidget(
|
||||
icon: actionIcon,
|
||||
isRounded: true,
|
||||
iconColor: strokeBaseDark,
|
||||
defaultColor: fillFaintDark,
|
||||
pressedColor: fillMutedDark,
|
||||
onTap: onTap,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
55
lib/ui/components/title_bar_title_widget.dart
Normal file
55
lib/ui/components/title_bar_title_widget.dart
Normal file
|
@ -0,0 +1,55 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/theme/ente_theme.dart';
|
||||
|
||||
class TitleBarTitleWidget extends StatelessWidget {
|
||||
final String? title;
|
||||
final bool isTitleH2;
|
||||
final IconData? icon;
|
||||
const TitleBarTitleWidget({
|
||||
this.title,
|
||||
this.isTitleH2 = false,
|
||||
this.icon,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final textTheme = getEnteTextTheme(context);
|
||||
final colorTheme = getEnteColorScheme(context);
|
||||
if (title != null) {
|
||||
if (icon != null) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
title!,
|
||||
style: textTheme.h3Bold,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Icon(icon, size: 20, color: colorTheme.strokeMuted),
|
||||
],
|
||||
);
|
||||
}
|
||||
if (isTitleH2) {
|
||||
return Text(
|
||||
title!,
|
||||
style: textTheme.h2Bold,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
);
|
||||
} else {
|
||||
return Text(
|
||||
title!,
|
||||
style: textTheme.h3Bold,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
}
|
146
lib/ui/components/title_bar_widget.dart
Normal file
146
lib/ui/components/title_bar_widget.dart
Normal file
|
@ -0,0 +1,146 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/theme/ente_theme.dart';
|
||||
import 'package:photos/ui/components/icon_button_widget.dart';
|
||||
|
||||
class TitleBarWidget extends StatelessWidget {
|
||||
final String? title;
|
||||
final String? caption;
|
||||
final Widget? flexibleSpaceTitle;
|
||||
final String? flexibleSpaceCaption;
|
||||
final List<Widget>? actionIcons;
|
||||
final bool isTitleH2WithoutLeading;
|
||||
final bool isFlexibleSpaceDisabled;
|
||||
const TitleBarWidget({
|
||||
this.title,
|
||||
this.caption,
|
||||
this.flexibleSpaceTitle,
|
||||
this.flexibleSpaceCaption,
|
||||
this.actionIcons,
|
||||
this.isTitleH2WithoutLeading = false,
|
||||
this.isFlexibleSpaceDisabled = false,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const toolbarHeight = 48.0;
|
||||
final textTheme = getEnteTextTheme(context);
|
||||
final colorTheme = getEnteColorScheme(context);
|
||||
return SliverAppBar(
|
||||
toolbarHeight: toolbarHeight,
|
||||
leadingWidth: 48,
|
||||
automaticallyImplyLeading: false,
|
||||
pinned: true,
|
||||
expandedHeight: 102,
|
||||
centerTitle: false,
|
||||
titleSpacing: 0,
|
||||
title: Padding(
|
||||
padding: EdgeInsets.only(left: isTitleH2WithoutLeading ? 16 : 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
title == null
|
||||
? const SizedBox.shrink()
|
||||
: Text(
|
||||
title!,
|
||||
style: isTitleH2WithoutLeading
|
||||
? textTheme.h2Bold
|
||||
: textTheme.largeBold,
|
||||
),
|
||||
caption == null || isTitleH2WithoutLeading
|
||||
? const SizedBox.shrink()
|
||||
: Text(
|
||||
caption!,
|
||||
style: textTheme.mini.copyWith(color: colorTheme.textMuted),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(4, 4, 12, 4),
|
||||
child: Row(
|
||||
children: _actionsWithPaddingInBetween(),
|
||||
),
|
||||
),
|
||||
],
|
||||
leading: isTitleH2WithoutLeading
|
||||
? null
|
||||
: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: IconButtonWidget(
|
||||
icon: Icons.arrow_back_outlined,
|
||||
isPrimary: true,
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
flexibleSpace: isFlexibleSpaceDisabled
|
||||
? null
|
||||
: FlexibleSpaceBar(
|
||||
background: SafeArea(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
const SizedBox(height: toolbarHeight),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 4,
|
||||
horizontal: 16,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
flexibleSpaceTitle == null
|
||||
? const SizedBox.shrink()
|
||||
: flexibleSpaceTitle!,
|
||||
flexibleSpaceCaption == null
|
||||
? const SizedBox.shrink()
|
||||
: Text(
|
||||
flexibleSpaceCaption!,
|
||||
style: textTheme.small.copyWith(
|
||||
color: colorTheme.textMuted,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_actionsWithPaddingInBetween() {
|
||||
if (actionIcons == null) {
|
||||
return <Widget>[const SizedBox.shrink()];
|
||||
}
|
||||
final actions = <Widget>[];
|
||||
bool addWhiteSpace = false;
|
||||
final length = actionIcons!.length;
|
||||
int index = 0;
|
||||
if (length == 0) {
|
||||
return <Widget>[const SizedBox.shrink()];
|
||||
}
|
||||
if (length == 1) {
|
||||
return actionIcons;
|
||||
}
|
||||
while (index < length) {
|
||||
if (!addWhiteSpace) {
|
||||
actions.add(actionIcons![index]);
|
||||
index++;
|
||||
addWhiteSpace = true;
|
||||
} else {
|
||||
actions.add(const SizedBox(width: 4));
|
||||
addWhiteSpace = false;
|
||||
}
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/ente_theme_data.dart';
|
||||
|
||||
typedef OnChangedCallBack = void Function(bool);
|
||||
typedef OnChangedCallBack = Future<void> Function();
|
||||
typedef ValueCallBack = bool Function();
|
||||
|
||||
class ToggleSwitchWidget extends StatefulWidget {
|
||||
final bool value;
|
||||
final ValueCallBack value;
|
||||
final OnChangedCallBack onChanged;
|
||||
const ToggleSwitchWidget({
|
||||
required this.value,
|
||||
|
@ -30,8 +31,11 @@ class _ToggleSwitchWidgetState extends State<ToggleSwitchWidget> {
|
|||
activeColor: enteColorScheme.primary400,
|
||||
inactiveTrackColor: enteColorScheme.fillMuted,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
value: widget.value,
|
||||
onChanged: widget.onChanged,
|
||||
value: widget.value.call(),
|
||||
onChanged: (value) async {
|
||||
await widget.onChanged.call();
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/ente_theme_data.dart';
|
||||
import 'package:photos/models/backup_status.dart';
|
||||
import 'package:photos/models/duplicate_files.dart';
|
||||
|
@ -11,11 +10,10 @@ import 'package:photos/services/deduplication_service.dart';
|
|||
import 'package:photos/services/sync_service.dart';
|
||||
import 'package:photos/theme/ente_theme.dart';
|
||||
import 'package:photos/ui/backup_folder_selection_page.dart';
|
||||
import 'package:photos/ui/common/dialogs.dart';
|
||||
import 'package:photos/ui/backup_settings_screen.dart';
|
||||
import 'package:photos/ui/components/captioned_text_widget.dart';
|
||||
import 'package:photos/ui/components/expandable_menu_item_widget.dart';
|
||||
import 'package:photos/ui/components/menu_item_widget.dart';
|
||||
import 'package:photos/ui/components/toggle_switch_widget.dart';
|
||||
import 'package:photos/ui/settings/common_settings.dart';
|
||||
import 'package:photos/ui/tools/deduplicate_page.dart';
|
||||
import 'package:photos/ui/tools/free_space_page.dart';
|
||||
|
@ -64,60 +62,21 @@ class BackupSectionWidgetState extends State<BackupSectionWidget> {
|
|||
sectionOptionSpacing,
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: const CaptionedTextWidget(
|
||||
title: "Backup over mobile data",
|
||||
),
|
||||
trailingSwitch: ToggleSwitchWidget(
|
||||
value: Configuration.instance.shouldBackupOverMobileData(),
|
||||
onChanged: (value) async {
|
||||
Configuration.instance.setBackupOverMobileData(value);
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: const CaptionedTextWidget(
|
||||
title: "Backup videos",
|
||||
),
|
||||
trailingSwitch: ToggleSwitchWidget(
|
||||
value: Configuration.instance.shouldBackupVideos(),
|
||||
onChanged: (value) async {
|
||||
Configuration.instance.setShouldBackupVideos(value);
|
||||
setState(() {});
|
||||
},
|
||||
title: "Backup settings",
|
||||
),
|
||||
pressedColor: getEnteColorScheme(context).fillFaint,
|
||||
trailingIcon: Icons.chevron_right_outlined,
|
||||
trailingIconIsMuted: true,
|
||||
onTap: () {
|
||||
routeToPage(
|
||||
context,
|
||||
const BackupSettingsScreen(),
|
||||
);
|
||||
},
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
];
|
||||
if (Platform.isIOS) {
|
||||
sectionOptions.addAll([
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: const CaptionedTextWidget(
|
||||
title: "Disable auto lock",
|
||||
),
|
||||
trailingSwitch: ToggleSwitchWidget(
|
||||
value: Configuration.instance.shouldKeepDeviceAwake(),
|
||||
onChanged: (value) async {
|
||||
if (value) {
|
||||
final choice = await showChoiceDialog(
|
||||
context,
|
||||
"Disable automatic screen lock when ente is running?",
|
||||
"This will ensure faster uploads by ensuring your device does not sleep when uploads are in progress.",
|
||||
firstAction: "No",
|
||||
secondAction: "Yes",
|
||||
);
|
||||
if (choice != DialogUserChoice.secondChoice) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
await Configuration.instance.setShouldKeepDeviceAwake(value);
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
]);
|
||||
}
|
||||
|
||||
sectionOptions.addAll(
|
||||
[
|
||||
MenuItemWidget(
|
||||
|
|
|
@ -73,8 +73,8 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
|
|||
),
|
||||
trailingSwitch: snapshot.hasData
|
||||
? ToggleSwitchWidget(
|
||||
value: snapshot.data,
|
||||
onChanged: (value) async {
|
||||
value: () => snapshot.data,
|
||||
onChanged: () async {
|
||||
final hasAuthenticated =
|
||||
await LocalAuthenticationService.instance
|
||||
.requestLocalAuthentication(
|
||||
|
@ -82,7 +82,7 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
|
|||
"Please authenticate to configure two-factor authentication",
|
||||
);
|
||||
if (hasAuthenticated) {
|
||||
if (value) {
|
||||
if (!snapshot.data) {
|
||||
UserService.instance.setupTwoFactor(context);
|
||||
} else {
|
||||
_disableTwoFactor();
|
||||
|
@ -106,18 +106,15 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
|
|||
title: "Lockscreen",
|
||||
),
|
||||
trailingSwitch: ToggleSwitchWidget(
|
||||
value: _config.shouldShowLockScreen(),
|
||||
onChanged: (value) async {
|
||||
final hasAuthenticated = await LocalAuthenticationService.instance
|
||||
value: () => _config.shouldShowLockScreen(),
|
||||
onChanged: () async {
|
||||
await LocalAuthenticationService.instance
|
||||
.requestLocalAuthForLockScreen(
|
||||
context,
|
||||
value,
|
||||
!_config.shouldShowLockScreen(),
|
||||
"Please authenticate to change lockscreen setting",
|
||||
"To enable lockscreen, please setup device passcode or screen lock in your system settings.",
|
||||
);
|
||||
if (hasAuthenticated) {
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
|
@ -131,81 +128,8 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
|
|||
title: "Hide from recents",
|
||||
),
|
||||
trailingSwitch: ToggleSwitchWidget(
|
||||
value: _config.shouldHideFromRecents(),
|
||||
onChanged: (value) async {
|
||||
if (value) {
|
||||
final AlertDialog alert = AlertDialog(
|
||||
title: const Text("Hide from recents?"),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: const [
|
||||
Text(
|
||||
"Hiding from the task switcher will prevent you from taking screenshots in this app.",
|
||||
style: TextStyle(
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
Padding(padding: EdgeInsets.all(8)),
|
||||
Text(
|
||||
"Are you sure?",
|
||||
style: TextStyle(
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text(
|
||||
"No",
|
||||
style: TextStyle(
|
||||
color:
|
||||
Theme.of(context).colorScheme.defaultTextColor,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context, rootNavigator: true)
|
||||
.pop('dialog');
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: Text(
|
||||
"Yes",
|
||||
style: TextStyle(
|
||||
color:
|
||||
Theme.of(context).colorScheme.defaultTextColor,
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
Navigator.of(context, rootNavigator: true)
|
||||
.pop('dialog');
|
||||
await _config.setShouldHideFromRecents(true);
|
||||
await FlutterWindowManager.addFlags(
|
||||
FlutterWindowManager.FLAG_SECURE,
|
||||
);
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return alert;
|
||||
},
|
||||
);
|
||||
} else {
|
||||
await _config.setShouldHideFromRecents(false);
|
||||
await FlutterWindowManager.clearFlags(
|
||||
FlutterWindowManager.FLAG_SECURE,
|
||||
);
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
value: () => _config.shouldHideFromRecents(),
|
||||
onChanged: _hideFromRecentsOnChanged,
|
||||
),
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
|
@ -284,4 +208,74 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
|
|||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _hideFromRecentsOnChanged() async {
|
||||
if (!_config.shouldHideFromRecents()) {
|
||||
final AlertDialog alert = AlertDialog(
|
||||
title: const Text("Hide from recents?"),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: const [
|
||||
Text(
|
||||
"Hiding from the task switcher will prevent you from taking screenshots in this app.",
|
||||
style: TextStyle(
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
Padding(padding: EdgeInsets.all(8)),
|
||||
Text(
|
||||
"Are you sure?",
|
||||
style: TextStyle(
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text(
|
||||
"No",
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.defaultTextColor,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context, rootNavigator: true).pop('dialog');
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: Text(
|
||||
"Yes",
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.defaultTextColor,
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
Navigator.of(context, rootNavigator: true).pop('dialog');
|
||||
await _config.setShouldHideFromRecents(true);
|
||||
await FlutterWindowManager.addFlags(
|
||||
FlutterWindowManager.FLAG_SECURE,
|
||||
);
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return alert;
|
||||
},
|
||||
);
|
||||
} else {
|
||||
await _config.setShouldHideFromRecents(false);
|
||||
await FlutterWindowManager.clearFlags(
|
||||
FlutterWindowManager.FLAG_SECURE,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue