Support for adding descriptions
File description
This commit is contained in:
commit
65bf985933
21 changed files with 417 additions and 208 deletions
|
@ -211,6 +211,10 @@ class File extends EnteFile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String? get caption {
|
||||||
|
return pubMagicMetadata?.caption;
|
||||||
|
}
|
||||||
|
|
||||||
String get thumbnailUrl {
|
String get thumbnailUrl {
|
||||||
final endpoint = Configuration.instance.getHttpEndpoint();
|
final endpoint = Configuration.instance.getHttpEndpoint();
|
||||||
if (endpoint != kDefaultProductionEndpoint ||
|
if (endpoint != kDefaultProductionEndpoint ||
|
||||||
|
|
|
@ -14,6 +14,7 @@ const subTypeKey = 'subType';
|
||||||
|
|
||||||
const pubMagicKeyEditedTime = 'editedTime';
|
const pubMagicKeyEditedTime = 'editedTime';
|
||||||
const pubMagicKeyEditedName = 'editedName';
|
const pubMagicKeyEditedName = 'editedName';
|
||||||
|
const pubMagicKeyCaption = "caption";
|
||||||
|
|
||||||
class MagicMetadata {
|
class MagicMetadata {
|
||||||
// 0 -> visible
|
// 0 -> visible
|
||||||
|
@ -39,8 +40,9 @@ class MagicMetadata {
|
||||||
class PubMagicMetadata {
|
class PubMagicMetadata {
|
||||||
int? editedTime;
|
int? editedTime;
|
||||||
String? editedName;
|
String? editedName;
|
||||||
|
String? caption;
|
||||||
|
|
||||||
PubMagicMetadata({this.editedTime, this.editedName});
|
PubMagicMetadata({this.editedTime, this.editedName, this.caption});
|
||||||
|
|
||||||
factory PubMagicMetadata.fromEncodedJson(String encodedJson) =>
|
factory PubMagicMetadata.fromEncodedJson(String encodedJson) =>
|
||||||
PubMagicMetadata.fromJson(jsonDecode(encodedJson));
|
PubMagicMetadata.fromJson(jsonDecode(encodedJson));
|
||||||
|
@ -53,6 +55,7 @@ class PubMagicMetadata {
|
||||||
return PubMagicMetadata(
|
return PubMagicMetadata(
|
||||||
editedTime: map[pubMagicKeyEditedTime],
|
editedTime: map[pubMagicKeyEditedTime],
|
||||||
editedName: map[pubMagicKeyEditedName],
|
editedName: map[pubMagicKeyEditedName],
|
||||||
|
caption: map[pubMagicKeyCaption],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,5 +22,6 @@ enum ResultType {
|
||||||
year,
|
year,
|
||||||
fileType,
|
fileType,
|
||||||
fileExtension,
|
fileExtension,
|
||||||
|
fileCaption,
|
||||||
event
|
event
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,6 +209,30 @@ class SearchService {
|
||||||
return searchResults;
|
return searchResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<GenericSearchResult>> getCaptionResults(
|
||||||
|
String query,
|
||||||
|
) async {
|
||||||
|
final List<GenericSearchResult> searchResults = [];
|
||||||
|
if (query.isEmpty) {
|
||||||
|
return searchResults;
|
||||||
|
}
|
||||||
|
final RegExp pattern = RegExp(query, caseSensitive: false);
|
||||||
|
final List<File> allFiles = await _getAllFiles();
|
||||||
|
final matchedFiles = allFiles
|
||||||
|
.where((e) => e.caption != null && pattern.hasMatch(e.caption))
|
||||||
|
.toList();
|
||||||
|
if (matchedFiles.isNotEmpty) {
|
||||||
|
searchResults.add(
|
||||||
|
GenericSearchResult(
|
||||||
|
ResultType.fileCaption,
|
||||||
|
query,
|
||||||
|
matchedFiles,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return searchResults;
|
||||||
|
}
|
||||||
|
|
||||||
Future<List<GenericSearchResult>> getFileExtensionResults(
|
Future<List<GenericSearchResult>> getFileExtensionResults(
|
||||||
String query,
|
String query,
|
||||||
) async {
|
) async {
|
||||||
|
|
|
@ -11,6 +11,7 @@ class EnteColorScheme {
|
||||||
// Backdrop Colors
|
// Backdrop Colors
|
||||||
final Color backdropBase;
|
final Color backdropBase;
|
||||||
final Color backdropBaseMute;
|
final Color backdropBaseMute;
|
||||||
|
final Color backdropFaint;
|
||||||
|
|
||||||
// Text Colors
|
// Text Colors
|
||||||
final Color textBase;
|
final Color textBase;
|
||||||
|
@ -53,6 +54,7 @@ class EnteColorScheme {
|
||||||
this.backgroundElevated2,
|
this.backgroundElevated2,
|
||||||
this.backdropBase,
|
this.backdropBase,
|
||||||
this.backdropBaseMute,
|
this.backdropBaseMute,
|
||||||
|
this.backdropFaint,
|
||||||
this.textBase,
|
this.textBase,
|
||||||
this.textMuted,
|
this.textMuted,
|
||||||
this.textFaint,
|
this.textFaint,
|
||||||
|
@ -84,7 +86,8 @@ const EnteColorScheme lightScheme = EnteColorScheme(
|
||||||
backgroundElevatedLight,
|
backgroundElevatedLight,
|
||||||
backgroundElevated2Light,
|
backgroundElevated2Light,
|
||||||
backdropBaseLight,
|
backdropBaseLight,
|
||||||
backdropBaseMuteLight,
|
backdropMutedLight,
|
||||||
|
backdropFaintLight,
|
||||||
textBaseLight,
|
textBaseLight,
|
||||||
textMutedLight,
|
textMutedLight,
|
||||||
textFaintLight,
|
textFaintLight,
|
||||||
|
@ -107,7 +110,8 @@ const EnteColorScheme darkScheme = EnteColorScheme(
|
||||||
backgroundElevatedDark,
|
backgroundElevatedDark,
|
||||||
backgroundElevated2Dark,
|
backgroundElevated2Dark,
|
||||||
backdropBaseDark,
|
backdropBaseDark,
|
||||||
backdropBaseMuteDark,
|
backdropMutedDark,
|
||||||
|
backdropFaintDark,
|
||||||
textBaseDark,
|
textBaseDark,
|
||||||
textMutedDark,
|
textMutedDark,
|
||||||
textFaintDark,
|
textFaintDark,
|
||||||
|
@ -136,10 +140,12 @@ const Color backgroundElevated2Dark = Color.fromRGBO(37, 37, 37, 1);
|
||||||
|
|
||||||
// Backdrop Colors
|
// Backdrop Colors
|
||||||
const Color backdropBaseLight = Color.fromRGBO(255, 255, 255, 0.75);
|
const Color backdropBaseLight = Color.fromRGBO(255, 255, 255, 0.75);
|
||||||
const Color backdropBaseMuteLight = Color.fromRGBO(255, 255, 255, 0.30);
|
const Color backdropMutedLight = Color.fromRGBO(255, 255, 255, 0.30);
|
||||||
|
const Color backdropFaintLight = Color.fromRGBO(255, 255, 255, 0.15);
|
||||||
|
|
||||||
const Color backdropBaseDark = Color.fromRGBO(0, 0, 0, 0.65);
|
const Color backdropBaseDark = Color.fromRGBO(0, 0, 0, 0.65);
|
||||||
const Color backdropBaseMuteDark = Color.fromRGBO(0, 0, 0, 0.20);
|
const Color backdropMutedDark = Color.fromRGBO(0, 0, 0, 0.20);
|
||||||
|
const Color backdropFaintDark = Color.fromRGBO(0, 0, 0, 0.08);
|
||||||
|
|
||||||
// Text Colors
|
// Text Colors
|
||||||
const Color textBaseLight = Color.fromRGBO(0, 0, 0, 1);
|
const Color textBaseLight = Color.fromRGBO(0, 0, 0, 1);
|
||||||
|
|
|
@ -29,7 +29,7 @@ class BackupSettingsScreen extends StatelessWidget {
|
||||||
actionIcons: [
|
actionIcons: [
|
||||||
IconButtonWidget(
|
IconButtonWidget(
|
||||||
icon: Icons.close_outlined,
|
icon: Icons.close_outlined,
|
||||||
isSecondary: true,
|
iconButtonType: IconButtonType.secondary,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
|
|
|
@ -20,7 +20,7 @@ class _HomeHeaderWidgetState extends State<HomeHeaderWidget> {
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
IconButtonWidget(
|
IconButtonWidget(
|
||||||
isPrimary: true,
|
iconButtonType: IconButtonType.primary,
|
||||||
icon: Icons.menu_outlined,
|
icon: Icons.menu_outlined,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Scaffold.of(context).openDrawer();
|
Scaffold.of(context).openDrawer();
|
||||||
|
|
|
@ -2,10 +2,14 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:photos/theme/colors.dart';
|
import 'package:photos/theme/colors.dart';
|
||||||
import 'package:photos/theme/ente_theme.dart';
|
import 'package:photos/theme/ente_theme.dart';
|
||||||
|
|
||||||
|
enum IconButtonType {
|
||||||
|
primary,
|
||||||
|
secondary,
|
||||||
|
rounded,
|
||||||
|
}
|
||||||
|
|
||||||
class IconButtonWidget extends StatefulWidget {
|
class IconButtonWidget extends StatefulWidget {
|
||||||
final bool isPrimary;
|
final IconButtonType iconButtonType;
|
||||||
final bool isSecondary;
|
|
||||||
final bool isRounded;
|
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
final bool disableGestureDetector;
|
final bool disableGestureDetector;
|
||||||
final VoidCallback? onTap;
|
final VoidCallback? onTap;
|
||||||
|
@ -14,9 +18,7 @@ class IconButtonWidget extends StatefulWidget {
|
||||||
final Color? iconColor;
|
final Color? iconColor;
|
||||||
const IconButtonWidget({
|
const IconButtonWidget({
|
||||||
required this.icon,
|
required this.icon,
|
||||||
this.isPrimary = false,
|
required this.iconButtonType,
|
||||||
this.isSecondary = false,
|
|
||||||
this.isRounded = false,
|
|
||||||
this.disableGestureDetector = false,
|
this.disableGestureDetector = false,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.defaultColor,
|
this.defaultColor,
|
||||||
|
@ -41,13 +43,12 @@ class _IconButtonWidgetState extends State<IconButtonWidget> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (!widget.isPrimary && !widget.isRounded && !widget.isSecondary) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
final colorTheme = getEnteColorScheme(context);
|
final colorTheme = getEnteColorScheme(context);
|
||||||
iconStateColor ??
|
iconStateColor ??
|
||||||
(iconStateColor = widget.defaultColor ??
|
(iconStateColor = widget.defaultColor ??
|
||||||
(widget.isRounded ? colorTheme.fillFaint : null));
|
(widget.iconButtonType == IconButtonType.rounded
|
||||||
|
? colorTheme.fillFaint
|
||||||
|
: null));
|
||||||
return widget.disableGestureDetector
|
return widget.disableGestureDetector
|
||||||
? _iconButton(colorTheme)
|
? _iconButton(colorTheme)
|
||||||
: GestureDetector(
|
: GestureDetector(
|
||||||
|
@ -72,7 +73,7 @@ class _IconButtonWidgetState extends State<IconButtonWidget> {
|
||||||
child: Icon(
|
child: Icon(
|
||||||
widget.icon,
|
widget.icon,
|
||||||
color: widget.iconColor ??
|
color: widget.iconColor ??
|
||||||
(widget.isSecondary
|
(widget.iconButtonType == IconButtonType.secondary
|
||||||
? colorTheme.strokeMuted
|
? colorTheme.strokeMuted
|
||||||
: colorTheme.strokeBase),
|
: colorTheme.strokeBase),
|
||||||
size: 24,
|
size: 24,
|
||||||
|
@ -85,7 +86,9 @@ class _IconButtonWidgetState extends State<IconButtonWidget> {
|
||||||
final colorTheme = getEnteColorScheme(context);
|
final colorTheme = getEnteColorScheme(context);
|
||||||
setState(() {
|
setState(() {
|
||||||
iconStateColor = widget.pressedColor ??
|
iconStateColor = widget.pressedColor ??
|
||||||
(widget.isRounded ? colorTheme.fillMuted : colorTheme.fillFaint);
|
(widget.iconButtonType == IconButtonType.rounded
|
||||||
|
? colorTheme.fillMuted
|
||||||
|
: colorTheme.fillFaint);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ class NotificationWarningWidget extends StatelessWidget {
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
IconButtonWidget(
|
IconButtonWidget(
|
||||||
icon: actionIcon,
|
icon: actionIcon,
|
||||||
isRounded: true,
|
iconButtonType: IconButtonType.rounded,
|
||||||
iconColor: strokeBaseDark,
|
iconColor: strokeBaseDark,
|
||||||
defaultColor: fillFaintDark,
|
defaultColor: fillFaintDark,
|
||||||
pressedColor: fillMutedDark,
|
pressedColor: fillMutedDark,
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'package:photos/theme/ente_theme.dart';
|
||||||
import 'package:photos/ui/components/icon_button_widget.dart';
|
import 'package:photos/ui/components/icon_button_widget.dart';
|
||||||
|
|
||||||
class TitleBarWidget extends StatelessWidget {
|
class TitleBarWidget extends StatelessWidget {
|
||||||
|
final IconButtonWidget? leading;
|
||||||
final String? title;
|
final String? title;
|
||||||
final String? caption;
|
final String? caption;
|
||||||
final Widget? flexibleSpaceTitle;
|
final Widget? flexibleSpaceTitle;
|
||||||
|
@ -10,7 +11,9 @@ class TitleBarWidget extends StatelessWidget {
|
||||||
final List<Widget>? actionIcons;
|
final List<Widget>? actionIcons;
|
||||||
final bool isTitleH2WithoutLeading;
|
final bool isTitleH2WithoutLeading;
|
||||||
final bool isFlexibleSpaceDisabled;
|
final bool isFlexibleSpaceDisabled;
|
||||||
|
final bool isOnTopOfScreen;
|
||||||
const TitleBarWidget({
|
const TitleBarWidget({
|
||||||
|
this.leading,
|
||||||
this.title,
|
this.title,
|
||||||
this.caption,
|
this.caption,
|
||||||
this.flexibleSpaceTitle,
|
this.flexibleSpaceTitle,
|
||||||
|
@ -18,6 +21,7 @@ class TitleBarWidget extends StatelessWidget {
|
||||||
this.actionIcons,
|
this.actionIcons,
|
||||||
this.isTitleH2WithoutLeading = false,
|
this.isTitleH2WithoutLeading = false,
|
||||||
this.isFlexibleSpaceDisabled = false,
|
this.isFlexibleSpaceDisabled = false,
|
||||||
|
this.isOnTopOfScreen = true,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -27,13 +31,14 @@ class TitleBarWidget extends StatelessWidget {
|
||||||
final textTheme = getEnteTextTheme(context);
|
final textTheme = getEnteTextTheme(context);
|
||||||
final colorTheme = getEnteColorScheme(context);
|
final colorTheme = getEnteColorScheme(context);
|
||||||
return SliverAppBar(
|
return SliverAppBar(
|
||||||
|
primary: isOnTopOfScreen ? true : false,
|
||||||
toolbarHeight: toolbarHeight,
|
toolbarHeight: toolbarHeight,
|
||||||
leadingWidth: 48,
|
leadingWidth: 48,
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
pinned: true,
|
pinned: true,
|
||||||
expandedHeight: 102,
|
expandedHeight: isFlexibleSpaceDisabled ? toolbarHeight : 102,
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
titleSpacing: 0,
|
titleSpacing: 4,
|
||||||
title: Padding(
|
title: Padding(
|
||||||
padding: EdgeInsets.only(left: isTitleH2WithoutLeading ? 16 : 0),
|
padding: EdgeInsets.only(left: isTitleH2WithoutLeading ? 16 : 0),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
@ -67,13 +72,14 @@ class TitleBarWidget extends StatelessWidget {
|
||||||
],
|
],
|
||||||
leading: isTitleH2WithoutLeading
|
leading: isTitleH2WithoutLeading
|
||||||
? null
|
? null
|
||||||
: IconButtonWidget(
|
: leading ??
|
||||||
icon: Icons.arrow_back_outlined,
|
IconButtonWidget(
|
||||||
isPrimary: true,
|
icon: Icons.arrow_back_outlined,
|
||||||
onTap: () {
|
iconButtonType: IconButtonType.primary,
|
||||||
Navigator.pop(context);
|
onTap: () {
|
||||||
},
|
Navigator.pop(context);
|
||||||
),
|
},
|
||||||
|
),
|
||||||
flexibleSpace: isFlexibleSpaceDisabled
|
flexibleSpace: isFlexibleSpaceDisabled
|
||||||
? null
|
? null
|
||||||
: FlexibleSpaceBar(
|
: FlexibleSpaceBar(
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||||
import 'package:page_transition/page_transition.dart';
|
import 'package:page_transition/page_transition.dart';
|
||||||
import 'package:photos/core/configuration.dart';
|
import 'package:photos/core/configuration.dart';
|
||||||
import 'package:photos/models/file.dart';
|
import 'package:photos/models/file.dart';
|
||||||
|
@ -12,6 +13,8 @@ import 'package:photos/models/magic_metadata.dart';
|
||||||
import 'package:photos/models/selected_files.dart';
|
import 'package:photos/models/selected_files.dart';
|
||||||
import 'package:photos/models/trash_file.dart';
|
import 'package:photos/models/trash_file.dart';
|
||||||
import 'package:photos/services/collections_service.dart';
|
import 'package:photos/services/collections_service.dart';
|
||||||
|
import 'package:photos/theme/colors.dart';
|
||||||
|
import 'package:photos/theme/ente_theme.dart';
|
||||||
import 'package:photos/ui/create_collection_page.dart';
|
import 'package:photos/ui/create_collection_page.dart';
|
||||||
import 'package:photos/ui/viewer/file/file_info_widget.dart';
|
import 'package:photos/ui/viewer/file/file_info_widget.dart';
|
||||||
import 'package:photos/utils/delete_file_util.dart';
|
import 'package:photos/utils/delete_file_util.dart';
|
||||||
|
@ -73,8 +76,13 @@ class FadingBottomBarState extends State<FadingBottomBar> {
|
||||||
Platform.isAndroid ? Icons.info_outline : CupertinoIcons.info,
|
Platform.isAndroid ? Icons.info_outline : CupertinoIcons.info,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
_displayInfo(widget.file);
|
await _displayInfo(widget.file);
|
||||||
|
safeRefresh(); //to instantly show the new caption if keypad is closed after pressing 'done' - here the caption will be updated before the bottom sheet is closed
|
||||||
|
await Future.delayed(
|
||||||
|
const Duration(milliseconds: 500),
|
||||||
|
); //Waiting for some time till the caption gets updated in db if the user closes the bottom sheet without pressing 'done'
|
||||||
|
safeRefresh();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -183,9 +191,31 @@ class FadingBottomBarState extends State<FadingBottomBar> {
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.only(bottom: safeAreaBottomPadding),
|
padding: EdgeInsets.only(bottom: safeAreaBottomPadding),
|
||||||
child: Row(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: children,
|
children: [
|
||||||
|
widget.file.caption?.isNotEmpty ?? false
|
||||||
|
? Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(
|
||||||
|
16,
|
||||||
|
28,
|
||||||
|
16,
|
||||||
|
12,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
widget.file.caption,
|
||||||
|
style: getEnteTextTheme(context)
|
||||||
|
.small
|
||||||
|
.copyWith(color: textBaseDark),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: children,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -249,11 +279,19 @@ class FadingBottomBarState extends State<FadingBottomBar> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _displayInfo(File file) async {
|
Future<void> _displayInfo(File file) async {
|
||||||
return showModalBottomSheet<void>(
|
final colorScheme = getEnteColorScheme(context);
|
||||||
|
return showBarModalBottomSheet(
|
||||||
|
topControl: const SizedBox.shrink(),
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0)),
|
||||||
|
backgroundColor: colorScheme.backgroundBase,
|
||||||
|
barrierColor: backdropFaintDark,
|
||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return FileInfoWidget(file);
|
return Padding(
|
||||||
|
padding:
|
||||||
|
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||||
|
child: FileInfoWidget(file),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
107
lib/ui/viewer/file/file_caption_widget.dart
Normal file
107
lib/ui/viewer/file/file_caption_widget.dart
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:photos/models/file.dart';
|
||||||
|
import 'package:photos/theme/ente_theme.dart';
|
||||||
|
import 'package:photos/utils/magic_util.dart';
|
||||||
|
|
||||||
|
class FileCaptionWidget extends StatefulWidget {
|
||||||
|
final File file;
|
||||||
|
const FileCaptionWidget({required this.file, super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FileCaptionWidget> createState() => _FileCaptionWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FileCaptionWidgetState extends State<FileCaptionWidget> {
|
||||||
|
int maxLength = 280;
|
||||||
|
int currentLength = 0;
|
||||||
|
final _textController = TextEditingController();
|
||||||
|
final _focusNode = FocusNode();
|
||||||
|
String? editedCaption;
|
||||||
|
String? hintText = "Add a description...";
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_focusNode.addListener(() {
|
||||||
|
final caption = widget.file.caption;
|
||||||
|
if (_focusNode.hasFocus && caption != null) {
|
||||||
|
_textController.text = caption;
|
||||||
|
editedCaption = caption;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
editedCaption = widget.file.caption;
|
||||||
|
if (editedCaption != null && editedCaption!.isNotEmpty) {
|
||||||
|
hintText = editedCaption;
|
||||||
|
}
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
if (editedCaption != null) {
|
||||||
|
editFileCaption(null, widget.file, editedCaption);
|
||||||
|
}
|
||||||
|
_textController.dispose();
|
||||||
|
_focusNode.removeListener(() {});
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final colorScheme = getEnteColorScheme(context);
|
||||||
|
final textTheme = getEnteTextTheme(context);
|
||||||
|
return TextField(
|
||||||
|
onEditingComplete: () async {
|
||||||
|
if (editedCaption != null) {
|
||||||
|
await editFileCaption(context, widget.file, editedCaption);
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_focusNode.unfocus();
|
||||||
|
},
|
||||||
|
controller: _textController,
|
||||||
|
focusNode: _focusNode,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
counterStyle: textTheme.mini.copyWith(color: colorScheme.textMuted),
|
||||||
|
counterText: currentLength > 99
|
||||||
|
? currentLength.toString() + " / " + maxLength.toString()
|
||||||
|
: "",
|
||||||
|
contentPadding: const EdgeInsets.all(16),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(2),
|
||||||
|
borderSide: const BorderSide(
|
||||||
|
width: 0,
|
||||||
|
style: BorderStyle.none,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(2),
|
||||||
|
borderSide: const BorderSide(
|
||||||
|
width: 0,
|
||||||
|
style: BorderStyle.none,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
filled: true,
|
||||||
|
fillColor: colorScheme.fillFaint,
|
||||||
|
hintText: hintText,
|
||||||
|
hintStyle: getEnteTextTheme(context)
|
||||||
|
.small
|
||||||
|
.copyWith(color: colorScheme.textMuted),
|
||||||
|
),
|
||||||
|
style: getEnteTextTheme(context).small,
|
||||||
|
cursorWidth: 1.5,
|
||||||
|
maxLength: maxLength,
|
||||||
|
minLines: 1,
|
||||||
|
maxLines: 6,
|
||||||
|
textCapitalization: TextCapitalization.sentences,
|
||||||
|
keyboardType: TextInputType.text,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
hintText = "Add a description...";
|
||||||
|
currentLength = value.length;
|
||||||
|
editedCaption = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,10 +9,13 @@ import 'package:photos/db/files_db.dart';
|
||||||
import "package:photos/ente_theme_data.dart";
|
import "package:photos/ente_theme_data.dart";
|
||||||
import "package:photos/models/file.dart";
|
import "package:photos/models/file.dart";
|
||||||
import "package:photos/models/file_type.dart";
|
import "package:photos/models/file_type.dart";
|
||||||
import 'package:photos/ui/common/DividerWithPadding.dart';
|
import 'package:photos/ui/components/divider_widget.dart';
|
||||||
|
import 'package:photos/ui/components/icon_button_widget.dart';
|
||||||
|
import 'package:photos/ui/components/title_bar_widget.dart';
|
||||||
import 'package:photos/ui/viewer/file/collections_list_of_file_widget.dart';
|
import 'package:photos/ui/viewer/file/collections_list_of_file_widget.dart';
|
||||||
import 'package:photos/ui/viewer/file/device_folders_list_of_file_widget.dart';
|
import 'package:photos/ui/viewer/file/device_folders_list_of_file_widget.dart';
|
||||||
import 'package:photos/ui/viewer/file/raw_exif_button.dart';
|
import 'package:photos/ui/viewer/file/file_caption_widget.dart';
|
||||||
|
import 'package:photos/ui/viewer/file/raw_exif_list_tile_widget.dart';
|
||||||
import "package:photos/utils/date_time_util.dart";
|
import "package:photos/utils/date_time_util.dart";
|
||||||
import "package:photos/utils/exif_util.dart";
|
import "package:photos/utils/exif_util.dart";
|
||||||
import "package:photos/utils/file_util.dart";
|
import "package:photos/utils/file_util.dart";
|
||||||
|
@ -90,9 +93,17 @@ class _FileInfoWidgetState extends State<FileInfoWidget> {
|
||||||
final bool showDimension =
|
final bool showDimension =
|
||||||
_exifData["resolution"] != null && _exifData["megaPixels"] != null;
|
_exifData["resolution"] != null && _exifData["megaPixels"] != null;
|
||||||
final listTiles = <Widget>[
|
final listTiles = <Widget>[
|
||||||
|
widget.file.uploadedFileID == null ||
|
||||||
|
Configuration.instance.getUserID() != file.ownerID
|
||||||
|
? const SizedBox.shrink()
|
||||||
|
: Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 8, bottom: 4),
|
||||||
|
child: FileCaptionWidget(file: widget.file),
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
|
horizontalTitleGap: 2,
|
||||||
leading: const Padding(
|
leading: const Padding(
|
||||||
padding: EdgeInsets.only(top: 8, left: 6),
|
padding: EdgeInsets.only(top: 8),
|
||||||
child: Icon(Icons.calendar_today_rounded),
|
child: Icon(Icons.calendar_today_rounded),
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
|
@ -121,17 +132,17 @@ class _FileInfoWidgetState extends State<FileInfoWidget> {
|
||||||
)
|
)
|
||||||
: const SizedBox.shrink(),
|
: const SizedBox.shrink(),
|
||||||
),
|
),
|
||||||
const DividerWithPadding(left: 70, right: 20),
|
|
||||||
ListTile(
|
ListTile(
|
||||||
|
horizontalTitleGap: 2,
|
||||||
leading: _isImage
|
leading: _isImage
|
||||||
? const Padding(
|
? const Padding(
|
||||||
padding: EdgeInsets.only(top: 8, left: 6),
|
padding: EdgeInsets.only(top: 8),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.image,
|
Icons.image,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: const Padding(
|
: const Padding(
|
||||||
padding: EdgeInsets.only(top: 8, left: 6),
|
padding: EdgeInsets.only(top: 8),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.video_camera_back,
|
Icons.video_camera_back,
|
||||||
size: 27,
|
size: 27,
|
||||||
|
@ -169,13 +180,10 @@ class _FileInfoWidgetState extends State<FileInfoWidget> {
|
||||||
icon: const Icon(Icons.edit),
|
icon: const Icon(Icons.edit),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const DividerWithPadding(left: 70, right: 20),
|
|
||||||
showExifListTile
|
showExifListTile
|
||||||
? ListTile(
|
? ListTile(
|
||||||
leading: const Padding(
|
horizontalTitleGap: 2,
|
||||||
padding: EdgeInsets.only(left: 6),
|
leading: const Icon(Icons.camera_rounded),
|
||||||
child: Icon(Icons.camera_rounded),
|
|
||||||
),
|
|
||||||
title: Text(_exifData["takenOnDevice"] ?? "--"),
|
title: Text(_exifData["takenOnDevice"] ?? "--"),
|
||||||
subtitle: Row(
|
subtitle: Row(
|
||||||
children: [
|
children: [
|
||||||
|
@ -207,27 +215,22 @@ class _FileInfoWidgetState extends State<FileInfoWidget> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: const SizedBox.shrink(),
|
: null,
|
||||||
showExifListTile
|
|
||||||
? const DividerWithPadding(left: 70, right: 20)
|
|
||||||
: const SizedBox.shrink(),
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 62,
|
height: 62,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Padding(
|
horizontalTitleGap: 0,
|
||||||
padding: EdgeInsets.only(left: 6),
|
leading: const Icon(Icons.folder_outlined),
|
||||||
child: Icon(Icons.folder_outlined),
|
|
||||||
),
|
|
||||||
title: fileIsBackedup
|
title: fileIsBackedup
|
||||||
? CollectionsListOfFileWidget(allCollectionIDsOfFile)
|
? CollectionsListOfFileWidget(allCollectionIDsOfFile)
|
||||||
: DeviceFoldersListOfFileWidget(allDeviceFoldersOfFile),
|
: DeviceFoldersListOfFileWidget(allDeviceFoldersOfFile),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const DividerWithPadding(left: 70, right: 20),
|
|
||||||
(file.uploadedFileID != null && file.updationTime != null)
|
(file.uploadedFileID != null && file.updationTime != null)
|
||||||
? ListTile(
|
? ListTile(
|
||||||
|
horizontalTitleGap: 2,
|
||||||
leading: const Padding(
|
leading: const Padding(
|
||||||
padding: EdgeInsets.only(top: 8, left: 6),
|
padding: EdgeInsets.only(top: 8),
|
||||||
child: Icon(Icons.cloud_upload_outlined),
|
child: Icon(Icons.cloud_upload_outlined),
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
|
@ -247,48 +250,53 @@ class _FileInfoWidgetState extends State<FileInfoWidget> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: const SizedBox.shrink(),
|
: null,
|
||||||
_isImage
|
_isImage ? RawExifListTileWidget(_exif, widget.file) : null,
|
||||||
? Padding(
|
|
||||||
padding: const EdgeInsets.fromLTRB(0, 24, 0, 16),
|
|
||||||
child: SafeArea(
|
|
||||||
child: RawExifButton(_exif, widget.file),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: const SizedBox(
|
|
||||||
height: 12,
|
|
||||||
)
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return Column(
|
listTiles.removeWhere(
|
||||||
mainAxisSize: MainAxisSize.min,
|
(element) => element == null,
|
||||||
children: [
|
);
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(10),
|
return SafeArea(
|
||||||
child: Row(
|
top: false,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
child: Scrollbar(
|
||||||
children: [
|
thickness: 4,
|
||||||
IconButton(
|
radius: const Radius.circular(2),
|
||||||
onPressed: () {
|
thumbVisibility: true,
|
||||||
Navigator.pop(context);
|
child: Padding(
|
||||||
},
|
padding: const EdgeInsets.all(8.0),
|
||||||
icon: const Icon(
|
child: CustomScrollView(
|
||||||
Icons.close,
|
shrinkWrap: true,
|
||||||
|
slivers: <Widget>[
|
||||||
|
TitleBarWidget(
|
||||||
|
isFlexibleSpaceDisabled: true,
|
||||||
|
title: "Details",
|
||||||
|
isOnTopOfScreen: false,
|
||||||
|
leading: IconButtonWidget(
|
||||||
|
icon: Icons.close_outlined,
|
||||||
|
iconButtonType: IconButtonType.primary,
|
||||||
|
onTap: () => Navigator.pop(context),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
SliverList(
|
||||||
Padding(
|
delegate: SliverChildBuilderDelegate(
|
||||||
padding: const EdgeInsets.only(bottom: 2),
|
(context, index) {
|
||||||
child: Text(
|
if (index.isOdd) {
|
||||||
"Details",
|
return index == 1
|
||||||
style: Theme.of(context).textTheme.bodyText1,
|
? const SizedBox.shrink()
|
||||||
|
: const DividerWidget(dividerType: DividerType.menu);
|
||||||
|
} else {
|
||||||
|
return listTiles[index ~/ 2];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
childCount: (listTiles.length * 2) - 1,
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
...listTiles
|
),
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,100 +0,0 @@
|
||||||
// @dart=2.9
|
|
||||||
|
|
||||||
import 'package:exif/exif.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:photos/ente_theme_data.dart';
|
|
||||||
import "package:photos/models/file.dart";
|
|
||||||
import 'package:photos/ui/viewer/file/exif_info_dialog.dart';
|
|
||||||
import 'package:photos/utils/toast_util.dart';
|
|
||||||
|
|
||||||
enum Status {
|
|
||||||
loading,
|
|
||||||
exifIsAvailable,
|
|
||||||
noExif,
|
|
||||||
}
|
|
||||||
|
|
||||||
class RawExifButton extends StatelessWidget {
|
|
||||||
final File file;
|
|
||||||
final Map<String, IfdTag> exif;
|
|
||||||
const RawExifButton(this.exif, this.file, {Key key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
Status exifStatus = Status.loading;
|
|
||||||
if (exif == null) {
|
|
||||||
exifStatus = Status.loading;
|
|
||||||
} else if (exif.isNotEmpty) {
|
|
||||||
exifStatus = Status.exifIsAvailable;
|
|
||||||
} else {
|
|
||||||
exifStatus = Status.noExif;
|
|
||||||
}
|
|
||||||
return GestureDetector(
|
|
||||||
onTap:
|
|
||||||
exifStatus == Status.loading || exifStatus == Status.exifIsAvailable
|
|
||||||
? () {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return ExifInfoDialog(file);
|
|
||||||
},
|
|
||||||
barrierColor: Colors.black87,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
: exifStatus == Status.noExif
|
|
||||||
? () {
|
|
||||||
showShortToast(context, "This image has no exif data");
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
child: Container(
|
|
||||||
height: 40,
|
|
||||||
width: 140,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.inverseBackgroundColor
|
|
||||||
.withOpacity(0.12),
|
|
||||||
borderRadius: const BorderRadius.all(
|
|
||||||
Radius.circular(20),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: exifStatus == Status.loading
|
|
||||||
? Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: const [
|
|
||||||
CupertinoActivityIndicator(
|
|
||||||
radius: 8,
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: 8,
|
|
||||||
),
|
|
||||||
Text('EXIF')
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: exifStatus == Status.exifIsAvailable
|
|
||||||
? Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: const [
|
|
||||||
Icon(Icons.feed_outlined),
|
|
||||||
SizedBox(
|
|
||||||
width: 8,
|
|
||||||
),
|
|
||||||
Text('Raw EXIF'),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: const [
|
|
||||||
Icon(Icons.feed_outlined),
|
|
||||||
SizedBox(
|
|
||||||
width: 8,
|
|
||||||
),
|
|
||||||
Text('No EXIF'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
71
lib/ui/viewer/file/raw_exif_list_tile_widget.dart
Normal file
71
lib/ui/viewer/file/raw_exif_list_tile_widget.dart
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
// @dart=2.9
|
||||||
|
|
||||||
|
import 'package:exif/exif.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:photos/ente_theme_data.dart';
|
||||||
|
import "package:photos/models/file.dart";
|
||||||
|
import 'package:photos/ui/viewer/file/exif_info_dialog.dart';
|
||||||
|
import 'package:photos/utils/toast_util.dart';
|
||||||
|
|
||||||
|
enum Status {
|
||||||
|
loading,
|
||||||
|
exifIsAvailable,
|
||||||
|
noExif,
|
||||||
|
}
|
||||||
|
|
||||||
|
class RawExifListTileWidget extends StatelessWidget {
|
||||||
|
final File file;
|
||||||
|
final Map<String, IfdTag> exif;
|
||||||
|
const RawExifListTileWidget(this.exif, this.file, {Key key})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Status exifStatus = Status.loading;
|
||||||
|
if (exif == null) {
|
||||||
|
exifStatus = Status.loading;
|
||||||
|
} else if (exif.isNotEmpty) {
|
||||||
|
exifStatus = Status.exifIsAvailable;
|
||||||
|
} else {
|
||||||
|
exifStatus = Status.noExif;
|
||||||
|
}
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: exifStatus == Status.exifIsAvailable
|
||||||
|
? () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return ExifInfoDialog(file);
|
||||||
|
},
|
||||||
|
barrierColor: Colors.black87,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
: exifStatus == Status.noExif
|
||||||
|
? () {
|
||||||
|
showShortToast(context, "This image has no exif data");
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
child: ListTile(
|
||||||
|
horizontalTitleGap: 2,
|
||||||
|
leading: const Padding(
|
||||||
|
padding: EdgeInsets.only(top: 8),
|
||||||
|
child: Icon(Icons.feed_outlined),
|
||||||
|
),
|
||||||
|
title: const Text("EXIF"),
|
||||||
|
subtitle: Text(
|
||||||
|
exifStatus == Status.loading
|
||||||
|
? "Loading EXIF data.."
|
||||||
|
: exifStatus == Status.exifIsAvailable
|
||||||
|
? "View all EXIF data"
|
||||||
|
: "No EXIF data",
|
||||||
|
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.defaultTextColor
|
||||||
|
.withOpacity(0.5),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,14 +29,13 @@ class NoResultWidget extends StatelessWidget {
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Center(
|
Container(
|
||||||
child: Container(
|
margin: const EdgeInsets.only(top: 8),
|
||||||
margin: const EdgeInsets.all(8),
|
child: const Text(
|
||||||
child: const Text(
|
"No results found",
|
||||||
"No results found",
|
textAlign: TextAlign.left,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -61,6 +60,7 @@ class NoResultWidget extends StatelessWidget {
|
||||||
\u2022 Types of files (e.g. "Videos", ".gif")
|
\u2022 Types of files (e.g. "Videos", ".gif")
|
||||||
\u2022 Years and months (e.g. "2022", "January")
|
\u2022 Years and months (e.g. "2022", "January")
|
||||||
\u2022 Holidays (e.g. "Christmas")
|
\u2022 Holidays (e.g. "Christmas")
|
||||||
|
\u2022 Photo descriptions (e.g. “#fun”)
|
||||||
''',
|
''',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
|
|
|
@ -125,6 +125,8 @@ class SearchResultWidget extends StatelessWidget {
|
||||||
return "Type";
|
return "Type";
|
||||||
case ResultType.fileExtension:
|
case ResultType.fileExtension:
|
||||||
return "File Extension";
|
return "File Extension";
|
||||||
|
case ResultType.fileCaption:
|
||||||
|
return "Description";
|
||||||
default:
|
default:
|
||||||
return type.name.toUpperCase();
|
return type.name.toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ class _SearchIconWidgetState extends State<SearchIconWidget> {
|
||||||
return Hero(
|
return Hero(
|
||||||
tag: "search_icon",
|
tag: "search_icon",
|
||||||
child: IconButtonWidget(
|
child: IconButtonWidget(
|
||||||
isPrimary: true,
|
iconButtonType: IconButtonType.primary,
|
||||||
icon: Icons.search,
|
icon: Icons.search,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
|
@ -196,6 +196,9 @@ class _SearchWidgetState extends State<SearchWidget> {
|
||||||
await _searchService.getFileTypeResults(query);
|
await _searchService.getFileTypeResults(query);
|
||||||
allResults.addAll(fileTypeSearchResults);
|
allResults.addAll(fileTypeSearchResults);
|
||||||
|
|
||||||
|
final fileCaptionResults = await _searchService.getCaptionResults(query);
|
||||||
|
allResults.addAll(fileCaptionResults);
|
||||||
|
|
||||||
final fileExtnResult =
|
final fileExtnResult =
|
||||||
await _searchService.getFileExtensionResults(query);
|
await _searchService.getFileExtensionResults(query);
|
||||||
allResults.addAll(fileExtnResult);
|
allResults.addAll(fileExtnResult);
|
||||||
|
|
|
@ -10,6 +10,7 @@ import 'package:photos/models/file.dart';
|
||||||
import 'package:photos/models/magic_metadata.dart';
|
import 'package:photos/models/magic_metadata.dart';
|
||||||
import 'package:photos/services/collections_service.dart';
|
import 'package:photos/services/collections_service.dart';
|
||||||
import 'package:photos/services/file_magic_service.dart';
|
import 'package:photos/services/file_magic_service.dart';
|
||||||
|
import 'package:photos/ui/common/progress_dialog.dart';
|
||||||
import 'package:photos/ui/common/rename_dialog.dart';
|
import 'package:photos/ui/common/rename_dialog.dart';
|
||||||
import 'package:photos/utils/dialog_util.dart';
|
import 'package:photos/utils/dialog_util.dart';
|
||||||
import 'package:photos/utils/toast_util.dart';
|
import 'package:photos/utils/toast_util.dart';
|
||||||
|
@ -123,7 +124,23 @@ Future<bool> editFilename(
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showToast(context, 'something went wrong');
|
showToast(context, 'Something went wrong');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> editFileCaption(
|
||||||
|
BuildContext context,
|
||||||
|
File file,
|
||||||
|
String caption,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
await _updatePublicMetadata(context, [file], pubMagicKeyCaption, caption);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
if (context != null) {
|
||||||
|
showToast(context, "Something went wrong");
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,19 +154,27 @@ Future<void> _updatePublicMetadata(
|
||||||
if (files.isEmpty) {
|
if (files.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final dialog = createProgressDialog(context, 'please wait...');
|
ProgressDialog dialog;
|
||||||
await dialog.show();
|
if (context != null) {
|
||||||
|
dialog = createProgressDialog(context, 'Please wait...');
|
||||||
|
await dialog.show();
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
final Map<String, dynamic> update = {key: value};
|
final Map<String, dynamic> update = {key: value};
|
||||||
await FileMagicService.instance.updatePublicMagicMetadata(files, update);
|
await FileMagicService.instance.updatePublicMagicMetadata(files, update);
|
||||||
showShortToast(context, 'done');
|
if (context != null) {
|
||||||
await dialog.hide();
|
showShortToast(context, 'Done');
|
||||||
|
await dialog.hide();
|
||||||
|
}
|
||||||
|
|
||||||
if (_shouldReloadGallery(key)) {
|
if (_shouldReloadGallery(key)) {
|
||||||
Bus.instance.fire(ForceReloadHomeGalleryEvent());
|
Bus.instance.fire(ForceReloadHomeGalleryEvent());
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
_logger.severe("failed to update $key = $value", e, s);
|
_logger.severe("failed to update $key = $value", e, s);
|
||||||
await dialog.hide();
|
if (context != null) {
|
||||||
|
await dialog.hide();
|
||||||
|
}
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -744,6 +744,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.2"
|
||||||
|
modal_bottom_sheet:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: modal_bottom_sheet
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
motionphoto:
|
motionphoto:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -79,6 +79,7 @@ dependencies:
|
||||||
lottie: ^1.2.2
|
lottie: ^1.2.2
|
||||||
media_extension:
|
media_extension:
|
||||||
git: "https://github.com/ente-io/media_extension.git"
|
git: "https://github.com/ente-io/media_extension.git"
|
||||||
|
modal_bottom_sheet: ^2.1.2
|
||||||
motionphoto:
|
motionphoto:
|
||||||
git: "https://github.com/ente-io/motionphoto.git"
|
git: "https://github.com/ente-io/motionphoto.git"
|
||||||
move_to_background: ^1.0.2
|
move_to_background: ^1.0.2
|
||||||
|
@ -91,7 +92,7 @@ dependencies:
|
||||||
path: #dart
|
path: #dart
|
||||||
path_provider: ^2.0.1
|
path_provider: ^2.0.1
|
||||||
pedantic: ^1.9.2
|
pedantic: ^1.9.2
|
||||||
photo_manager: ^2.4.1
|
photo_manager: ^2.5.0
|
||||||
photo_view: ^0.14.0
|
photo_view: ^0.14.0
|
||||||
pinput: ^1.2.2
|
pinput: ^1.2.2
|
||||||
provider: ^6.0.0
|
provider: ^6.0.0
|
||||||
|
|
Loading…
Add table
Reference in a new issue