mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: add uploaded at & improvements
This commit is contained in:
parent
61d30fad6c
commit
fd3f850d63
@ -7,12 +7,14 @@ import 'package:appflowy/plugins/document/presentation/banner.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_drop_manager.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_notification.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_page.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/paste_from_file.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/paste_from_image.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/cover/document_immersive_cover.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/custom_image_block_component/custom_image_block_component.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/multi_image_block_component/multi_image_block_component.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
|
||||
import 'package:appflowy/shared/patterns/common_patterns.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/application/action_navigation/action_navigation_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/action_navigation/navigation_action.dart';
|
||||
@ -20,6 +22,7 @@ import 'package:appflowy/workspace/application/view/prelude.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
||||
import 'package:cross_file/cross_file.dart';
|
||||
import 'package:desktop_drop/desktop_drop.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||
@ -195,9 +198,27 @@ class _DocumentPageState extends State<DocumentPage>
|
||||
}
|
||||
|
||||
final isLocalMode = context.read<DocumentBloc>().isLocalMode;
|
||||
|
||||
final List<XFile> imageFiles = [];
|
||||
final List<XFile> otherfiles = [];
|
||||
for (final file in details.files) {
|
||||
if (file.mimeType?.startsWith('image/') ??
|
||||
false || imgExtensionRegex.hasMatch(file.name)) {
|
||||
imageFiles.add(file);
|
||||
} else {
|
||||
otherfiles.add(file);
|
||||
}
|
||||
}
|
||||
|
||||
await editorState!.dropImages(
|
||||
data.dropTarget!,
|
||||
details.files,
|
||||
imageFiles,
|
||||
widget.view.id,
|
||||
isLocalMode,
|
||||
);
|
||||
await editorState!.dropFiles(
|
||||
data.dropTarget!,
|
||||
otherfiles,
|
||||
widget.view.id,
|
||||
isLocalMode,
|
||||
);
|
||||
|
@ -0,0 +1,40 @@
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/file/file_block.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/file/file_util.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:cross_file/cross_file.dart';
|
||||
|
||||
extension PasteFromFile on EditorState {
|
||||
Future<void> dropFiles(
|
||||
Node dropNode,
|
||||
List<XFile> files,
|
||||
String documentId,
|
||||
bool isLocalMode,
|
||||
) async {
|
||||
for (final file in files) {
|
||||
String? path;
|
||||
FileUrlType? type;
|
||||
if (isLocalMode) {
|
||||
path = await saveFileToLocalStorage(file.path);
|
||||
type = FileUrlType.local;
|
||||
} else {
|
||||
(path, _) = await saveFileToCloudStorage(file.path, documentId);
|
||||
type = FileUrlType.cloud;
|
||||
}
|
||||
|
||||
if (path == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final t = transaction
|
||||
..insertNode(
|
||||
dropNode.path,
|
||||
fileNode(
|
||||
url: path,
|
||||
type: type,
|
||||
name: file.name,
|
||||
),
|
||||
);
|
||||
await apply(t);
|
||||
}
|
||||
}
|
||||
}
|
@ -90,12 +90,15 @@ enum FileUrlType {
|
||||
Node fileNode({
|
||||
required String url,
|
||||
FileUrlType type = FileUrlType.local,
|
||||
String? name,
|
||||
}) {
|
||||
return Node(
|
||||
type: FileBlockKeys.type,
|
||||
attributes: {
|
||||
FileBlockKeys.url: url,
|
||||
FileBlockKeys.urlType: type.toIntValue(),
|
||||
FileBlockKeys.name: name,
|
||||
FileBlockKeys.uploadedAt: DateTime.now().millisecondsSinceEpoch,
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -149,7 +152,6 @@ class FileBlockComponentState extends State<FileBlockComponent>
|
||||
final showActionsNotifier = ValueNotifier<bool>(false);
|
||||
final controller = PopoverController();
|
||||
final menuController = PopoverController();
|
||||
final menuMutex = PopoverMutex();
|
||||
|
||||
late final editorState = Provider.of<EditorState>(context, listen: false);
|
||||
|
||||
@ -157,26 +159,6 @@ class FileBlockComponentState extends State<FileBlockComponent>
|
||||
bool isDragging = false;
|
||||
bool isHovering = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
final url = node.attributes[FileBlockKeys.url] as String?;
|
||||
if (url != null && url.isNotEmpty) {
|
||||
// If the name attribute is not set, extract the file name from the url.
|
||||
final name = node.attributes[FileBlockKeys.name] as String?;
|
||||
if (name == null || name.isEmpty) {
|
||||
final name = Uri.tryParse(url)?.pathSegments.last ?? url;
|
||||
final attributes = node.attributes;
|
||||
attributes[FileBlockKeys.name] = name;
|
||||
|
||||
final transaction = editorState.transaction;
|
||||
transaction.updateNode(node, attributes);
|
||||
editorState.apply(transaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
dropManagerState = context.read<EditorDropManagerState>();
|
||||
@ -202,8 +184,9 @@ class FileBlockComponentState extends State<FileBlockComponent>
|
||||
opaque: false,
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap:
|
||||
url != null && url.isNotEmpty ? () => afLaunchUrlString(url) : null,
|
||||
onTap: url != null && url.isNotEmpty
|
||||
? () => afLaunchUrlString(url)
|
||||
: controller.show,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: isHovering
|
||||
@ -328,7 +311,6 @@ class FileBlockComponentState extends State<FileBlockComponent>
|
||||
onTap: menuController.show,
|
||||
child: AppFlowyPopover(
|
||||
controller: menuController,
|
||||
mutex: menuMutex,
|
||||
triggerActions: PopoverTriggerFlags.none,
|
||||
direction: PopoverDirection.bottomWithRightAligned,
|
||||
onClose: () {
|
||||
@ -406,6 +388,7 @@ class FileBlockComponentState extends State<FileBlockComponent>
|
||||
FileBlockKeys.url: url,
|
||||
FileBlockKeys.urlType: urlType.toIntValue(),
|
||||
FileBlockKeys.name: name,
|
||||
FileBlockKeys.uploadedAt: DateTime.now().millisecondsSinceEpoch,
|
||||
});
|
||||
await editorState.apply(transaction);
|
||||
}
|
||||
@ -428,6 +411,7 @@ class FileBlockComponentState extends State<FileBlockComponent>
|
||||
FileBlockKeys.url: url,
|
||||
FileBlockKeys.urlType: FileUrlType.network.toIntValue(),
|
||||
FileBlockKeys.name: name,
|
||||
FileBlockKeys.uploadedAt: DateTime.now().millisecondsSinceEpoch,
|
||||
});
|
||||
await editorState.apply(transaction);
|
||||
}
|
||||
|
@ -3,12 +3,15 @@ import 'package:flutter/material.dart';
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/file/file_block.dart';
|
||||
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
||||
import 'package:appflowy/workspace/application/settings/date_time/date_format_ext.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class FileBlockMenu extends StatefulWidget {
|
||||
const FileBlockMenu({
|
||||
@ -41,9 +44,25 @@ class _FileBlockMenuState extends State<FileBlockMenu> {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
errorMessage.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final uploadedAtInMS =
|
||||
widget.node.attributes[FileBlockKeys.uploadedAt] as int?;
|
||||
final uploadedAt = uploadedAtInMS != null
|
||||
? DateTime.fromMillisecondsSinceEpoch(uploadedAtInMS)
|
||||
: null;
|
||||
final dateFormat = context.read<AppearanceSettingsCubit>().state.dateFormat;
|
||||
final urlType =
|
||||
FileUrlType.fromIntValue(widget.node.attributes[FileBlockKeys.urlType]);
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
HoverButton(
|
||||
@ -84,6 +103,25 @@ class _FileBlockMenuState extends State<FileBlockMenu> {
|
||||
widget.controller.close();
|
||||
},
|
||||
),
|
||||
if (uploadedAt != null) ...[
|
||||
const Divider(height: 12),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: FlowyText.regular(
|
||||
[FileUrlType.cloud, FileUrlType.local].contains(urlType)
|
||||
? LocaleKeys.document_plugins_file_uploadedAt.tr(
|
||||
args: [dateFormat.formatDate(uploadedAt, false)],
|
||||
)
|
||||
: LocaleKeys.document_plugins_file_linkedAt.tr(
|
||||
args: [dateFormat.formatDate(uploadedAt, false)],
|
||||
),
|
||||
fontSize: 14,
|
||||
maxLines: 2,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
),
|
||||
const VSpace(2),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -133,6 +171,7 @@ class _RenameTextFieldState extends State<_RenameTextField> {
|
||||
@override
|
||||
void dispose() {
|
||||
widget.errorMessage.removeListener(_setState);
|
||||
widget.nameController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
@ -1580,7 +1580,9 @@
|
||||
"title": "Rename file",
|
||||
"description": "Enter the new name for this file",
|
||||
"nameEmptyError": "File name cannot be left empty."
|
||||
}
|
||||
},
|
||||
"uploadedAt": "Uploaded on {}",
|
||||
"linkedAt": "Link added on {}"
|
||||
}
|
||||
},
|
||||
"outlineBlock": {
|
||||
|
Loading…
Reference in New Issue
Block a user