mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: adjust cover plugin and support recent section on mobile platform (#3921)
This commit is contained in:
@ -529,14 +529,12 @@ class ColorItem extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
customBorder: const RoundedRectangleBorder(
|
||||
borderRadius: Corners.s6Border,
|
||||
),
|
||||
hoverColor: hoverColor,
|
||||
onTap: () => onTap(option.colorHex),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 10.0),
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 10.0),
|
||||
child: InkWell(
|
||||
customBorder: const CircleBorder(),
|
||||
hoverColor: hoverColor,
|
||||
onTap: () => onTap(option.colorHex),
|
||||
child: SizedBox.square(
|
||||
dimension: 25,
|
||||
child: DecoratedBox(
|
||||
|
@ -2,19 +2,23 @@ import 'dart:io';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
||||
import 'package:appflowy/plugins/base/emoji/emoji_picker_screen.dart';
|
||||
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_listener.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart' hide UploadImageMenu;
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:string_validator/string_validator.dart';
|
||||
|
||||
import 'cover_editor.dart';
|
||||
|
||||
@ -262,7 +266,9 @@ class _DocumentHeaderToolbarState extends State<DocumentHeaderToolbar> {
|
||||
FlowyButton(
|
||||
leftIconSize: const Size.square(18),
|
||||
onTap: () => widget.onCoverChanged(
|
||||
cover: (CoverType.asset, builtInAssetImages.first),
|
||||
cover: PlatformExtension.isDesktopOrWeb
|
||||
? (CoverType.asset, builtInAssetImages.first)
|
||||
: (CoverType.color, '0xffe8e0ff'),
|
||||
),
|
||||
useIntrinsicWidth: true,
|
||||
leftIcon: const FlowySvg(FlowySvgs.image_s),
|
||||
@ -373,6 +379,12 @@ class DocumentCoverState extends State<DocumentCover> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PlatformExtension.isDesktopOrWeb
|
||||
? _buildDesktopCover()
|
||||
: _buildMobileCover();
|
||||
}
|
||||
|
||||
Widget _buildDesktopCover() {
|
||||
return SizedBox(
|
||||
height: kCoverHeight,
|
||||
child: MouseRegion(
|
||||
@ -393,10 +405,82 @@ class DocumentCoverState extends State<DocumentCover> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMobileCover() {
|
||||
return SizedBox(
|
||||
height: kCoverHeight,
|
||||
child: Stack(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: double.infinity,
|
||||
width: double.infinity,
|
||||
child: _buildCoverImage(),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 8,
|
||||
right: 12,
|
||||
child: RoundedTextButton(
|
||||
onPressed: () {
|
||||
showFlowyMobileBottomSheet(
|
||||
context,
|
||||
title: LocaleKeys.document_plugins_cover_changeCover.tr(),
|
||||
builder: (context) {
|
||||
return ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxHeight: 340,
|
||||
minHeight: 80,
|
||||
),
|
||||
child: UploadImageMenu(
|
||||
supportTypes: const [
|
||||
UploadImageType.color,
|
||||
UploadImageType.local,
|
||||
UploadImageType.url,
|
||||
UploadImageType.unsplash,
|
||||
],
|
||||
onSelectedLocalImage: (path) async {
|
||||
context.pop();
|
||||
widget.onCoverChanged(CoverType.file, path);
|
||||
},
|
||||
onSelectedAIImage: (_) {
|
||||
throw UnimplementedError();
|
||||
},
|
||||
onSelectedNetworkImage: (url) async {
|
||||
context.pop();
|
||||
widget.onCoverChanged(CoverType.file, url);
|
||||
},
|
||||
onSelectedColor: (color) {
|
||||
context.pop();
|
||||
widget.onCoverChanged(CoverType.color, color);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
fillColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
width: 120,
|
||||
height: 32,
|
||||
title: LocaleKeys.document_plugins_cover_changeCover.tr(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCoverImage() {
|
||||
final detail = widget.coverDetails;
|
||||
if (detail == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
switch (widget.coverType) {
|
||||
case CoverType.file:
|
||||
final imageFile = File(widget.coverDetails ?? "");
|
||||
if (isURL(detail)) {
|
||||
return CachedNetworkImage(
|
||||
imageUrl: detail,
|
||||
fit: BoxFit.cover,
|
||||
);
|
||||
}
|
||||
final imageFile = File(detail);
|
||||
if (!imageFile.existsSync()) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
widget.onCoverChanged(CoverType.none, null);
|
||||
|
@ -0,0 +1,41 @@
|
||||
import 'package:appflowy/mobile/presentation/base/app_bar_actions.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class ImagePickerPage extends StatefulWidget {
|
||||
const ImagePickerPage({
|
||||
super.key,
|
||||
// required this.onSelected,
|
||||
});
|
||||
|
||||
// final void Function(EmojiPickerResult) onSelected;
|
||||
|
||||
@override
|
||||
State<ImagePickerPage> createState() => _ImagePickerPageState();
|
||||
}
|
||||
|
||||
class _ImagePickerPageState extends State<ImagePickerPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
titleSpacing: 0,
|
||||
title: const FlowyText.semibold(
|
||||
'Page icon',
|
||||
fontSize: 14.0,
|
||||
),
|
||||
leading: AppBarBackButton(
|
||||
onTap: () => context.pop(),
|
||||
),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: UploadImageMenu(
|
||||
onSubmitted: (_) {},
|
||||
onUpload: (_) {},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/flowy_image_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MobileImagePickerScreen extends StatelessWidget {
|
||||
static const routeName = '/image_picker';
|
||||
|
||||
const MobileImagePickerScreen({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const ImagePickerPage();
|
||||
}
|
||||
}
|
@ -1,12 +1,15 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/cover_editor.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/embed_image_url_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/open_ai_image_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/stability_ai_image_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/unsplash_image_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_file_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||
import 'package:appflowy/user/application/user_service.dart';
|
||||
import 'package:appflowy/util/platform_extension.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart' hide ColorOption;
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -16,7 +19,8 @@ enum UploadImageType {
|
||||
url,
|
||||
unsplash,
|
||||
stabilityAI,
|
||||
openAI;
|
||||
openAI,
|
||||
color;
|
||||
|
||||
String get description {
|
||||
switch (this) {
|
||||
@ -30,6 +34,8 @@ enum UploadImageType {
|
||||
return LocaleKeys.document_imageBlock_ai_label.tr();
|
||||
case UploadImageType.stabilityAI:
|
||||
return LocaleKeys.document_imageBlock_stability_ai_label.tr();
|
||||
case UploadImageType.color:
|
||||
return LocaleKeys.document_plugins_cover_colors.tr();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -40,12 +46,14 @@ class UploadImageMenu extends StatefulWidget {
|
||||
required this.onSelectedLocalImage,
|
||||
required this.onSelectedAIImage,
|
||||
required this.onSelectedNetworkImage,
|
||||
this.onSelectedColor,
|
||||
this.supportTypes = UploadImageType.values,
|
||||
});
|
||||
|
||||
final void Function(String? path) onSelectedLocalImage;
|
||||
final void Function(String url) onSelectedAIImage;
|
||||
final void Function(String url) onSelectedNetworkImage;
|
||||
final void Function(String color)? onSelectedColor;
|
||||
final List<UploadImageType> supportTypes;
|
||||
|
||||
@override
|
||||
@ -128,18 +136,23 @@ class _UploadImageMenuState extends State<UploadImageMenu> {
|
||||
}
|
||||
|
||||
Widget _buildTab() {
|
||||
final type = UploadImageType.values[currentTabIndex];
|
||||
final constraints =
|
||||
PlatformExtension.isMobile ? const BoxConstraints(minHeight: 92) : null;
|
||||
final type = values[currentTabIndex];
|
||||
switch (type) {
|
||||
case UploadImageType.local:
|
||||
return Padding(
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
alignment: Alignment.center,
|
||||
constraints: constraints,
|
||||
child: UploadImageFileWidget(
|
||||
onPickFile: widget.onSelectedLocalImage,
|
||||
),
|
||||
);
|
||||
case UploadImageType.url:
|
||||
return Padding(
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
constraints: constraints,
|
||||
child: EmbedImageUrlWidget(
|
||||
onSubmit: widget.onSelectedNetworkImage,
|
||||
),
|
||||
@ -156,8 +169,9 @@ class _UploadImageMenuState extends State<UploadImageMenu> {
|
||||
case UploadImageType.openAI:
|
||||
return supportOpenAI
|
||||
? Expanded(
|
||||
child: Padding(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
constraints: constraints,
|
||||
child: OpenAIImageWidget(
|
||||
onSelectNetworkImage: widget.onSelectedAIImage,
|
||||
),
|
||||
@ -172,7 +186,7 @@ class _UploadImageMenuState extends State<UploadImageMenu> {
|
||||
case UploadImageType.stabilityAI:
|
||||
return supportStabilityAI
|
||||
? Expanded(
|
||||
child: Padding(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: StabilityAIImageWidget(
|
||||
onSelectImage: widget.onSelectedLocalImage,
|
||||
@ -186,6 +200,28 @@ class _UploadImageMenuState extends State<UploadImageMenu> {
|
||||
.tr(),
|
||||
),
|
||||
);
|
||||
case UploadImageType.color:
|
||||
final theme = Theme.of(context);
|
||||
return Container(
|
||||
constraints: constraints,
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
alignment: Alignment.center,
|
||||
child: CoverColorPicker(
|
||||
pickerBackgroundColor: theme.cardColor,
|
||||
pickerItemHoverColor: theme.hoverColor,
|
||||
backgroundColorOptions: FlowyTint.values
|
||||
.map<ColorOption>(
|
||||
(t) => ColorOption(
|
||||
colorHex: t.color(context).toHex(),
|
||||
name: t.tintName(AppFlowyEditorL10n.current),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onSubmittedBackgroundColorHex: (color) {
|
||||
widget.onSelectedColor?.call(color);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user