feat: adjust cover plugin and support recent section on mobile platform (#3921)

This commit is contained in:
Lucas.Xu
2023-11-13 10:07:46 +08:00
committed by GitHub
parent 765103dd22
commit 7cee8e392f
22 changed files with 734 additions and 224 deletions

View File

@ -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(

View File

@ -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);

View File

@ -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: (_) {},
),
),
);
}
}

View File

@ -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();
}
}

View File

@ -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);
},
),
);
}
}
}