feat: publish page to the web

This commit is contained in:
Lucas.Xu 2024-06-21 15:13:17 +08:00 committed by Kilu
parent 1c8913eb79
commit 3edcc08543
8 changed files with 375 additions and 2 deletions

View File

@ -0,0 +1,215 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.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';
class PublishTab extends StatefulWidget {
const PublishTab({super.key});
@override
State<PublishTab> createState() => _PublishTabState();
}
class _PublishTabState extends State<PublishTab> {
bool isPublished = false;
@override
Widget build(BuildContext context) {
return isPublished
? _PublishedWidget(onUnPublish: () {}, onVisitSite: () {})
: _UnPublishWidget(onPublish: () => setState(() => isPublished = true));
}
}
class _PublishedWidget extends StatefulWidget {
const _PublishedWidget({
required this.onVisitSite,
required this.onUnPublish,
});
final VoidCallback onVisitSite;
final VoidCallback onUnPublish;
@override
State<_PublishedWidget> createState() => _PublishedWidgetState();
}
class _PublishedWidgetState extends State<_PublishedWidget> {
final controller = TextEditingController();
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const VSpace(16),
const _PublishTabHeader(),
const VSpace(16),
_PublishUrl(
controller: controller,
onCopy: (url) {},
onSubmitted: (url) {},
),
const VSpace(16),
Row(
mainAxisSize: MainAxisSize.min,
children: [
_buildButton(
context,
name: LocaleKeys.shareAction_unPublish.tr(),
borderColor: const Color(0x1E14171B),
onTap: widget.onUnPublish,
),
const Spacer(),
_buildButton(
context,
name: LocaleKeys.shareAction_visitSite.tr(),
backgroundColor: Theme.of(context).colorScheme.primary,
textColor: Colors.white,
onTap: () {},
),
],
),
],
);
}
Widget _buildButton(
BuildContext context, {
required String name,
Color? backgroundColor,
Color borderColor = Colors.transparent,
Color? textColor,
required VoidCallback onTap,
}) {
return SizedBox(
height: 36,
width: 189,
child: FlowyButton(
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: borderColor),
),
text: FlowyText.regular(
name,
textAlign: TextAlign.center,
color: textColor,
),
onTap: onTap,
),
);
}
}
class _UnPublishWidget extends StatelessWidget {
const _UnPublishWidget({
required this.onPublish,
});
final VoidCallback onPublish;
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const VSpace(16),
const _PublishTabHeader(),
const VSpace(16),
RoundedTextButton(
height: 36,
title: LocaleKeys.shareAction_publish.tr(),
padding: const EdgeInsets.symmetric(vertical: 9.0),
fontSize: 14.0,
textColor: Theme.of(context).colorScheme.onPrimary,
onPressed: onPublish,
),
],
);
}
}
class _PublishTabHeader extends StatelessWidget {
const _PublishTabHeader();
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const FlowySvg(FlowySvgs.share_publish_s),
const HSpace(6),
FlowyText(LocaleKeys.shareAction_publishToTheWeb.tr()),
],
),
const VSpace(4),
FlowyText.regular(
LocaleKeys.shareAction_publishToTheWebHint.tr(),
fontSize: 12,
maxLines: 3,
color: Theme.of(context).hintColor,
),
],
);
}
}
class _PublishUrl extends StatelessWidget {
const _PublishUrl({
required this.controller,
required this.onCopy,
required this.onSubmitted,
});
final TextEditingController controller;
final void Function(String url) onCopy;
final void Function(String url) onSubmitted;
@override
Widget build(BuildContext context) {
return SizedBox(
height: 36,
child: FlowyTextField(
autoFocus: false,
controller: controller,
text: 'http:/appflowy.com/vinh/open-positions',
suffixIcon: _buildCopyLinkIcon(),
),
);
}
Widget _buildCopyLinkIcon() {
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () => onCopy(controller.text),
child: Container(
width: 36,
height: 36,
alignment: Alignment.center,
padding: const EdgeInsets.all(10),
decoration: const BoxDecoration(
border: Border(left: BorderSide(color: Color(0x141F2329))),
),
child: const FlowySvg(
FlowySvgs.m_toolbar_link_m,
),
),
),
);
}
}

View File

@ -1,6 +1,7 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/application/document_share_bloc.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart';
import 'package:appflowy/plugins/document/presentation/share/share_menu.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/util/string_extension.dart';
import 'package:appflowy/workspace/application/export/document_exporter.dart';
@ -13,6 +14,7 @@ import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/file_picker/file_picker_service.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:flutter_bloc/flutter_bloc.dart';
@ -43,7 +45,23 @@ class DocumentShareButton extends StatelessWidget {
child: BlocBuilder<DocumentShareBloc, DocumentShareState>(
builder: (context, state) => SizedBox(
height: 32.0,
child: IntrinsicWidth(child: ShareActionList(view: view)),
child: IntrinsicWidth(
child: AppFlowyPopover(
direction: PopoverDirection.bottomWithRightAligned,
constraints: const BoxConstraints(
maxWidth: 422,
),
margin: const EdgeInsets.all(16),
offset: const Offset(0, 8),
popupBuilder: (context) => const ShareMenu(),
child: RoundedTextButton(
title: LocaleKeys.shareAction_buttonText.tr(),
padding: const EdgeInsets.symmetric(horizontal: 12.0),
fontSize: 14.0,
textColor: Theme.of(context).colorScheme.onPrimary,
),
),
),
),
),
),

View File

@ -0,0 +1,120 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:custom_sliding_segmented_control/custom_sliding_segmented_control.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'publish_tab.dart';
enum ShareMenuTab {
share,
publish,
exportAs;
static List<ShareMenuTab> supportedTabs = [
ShareMenuTab.share,
ShareMenuTab.publish,
ShareMenuTab.exportAs,
];
String get i18n {
switch (this) {
case ShareMenuTab.share:
return 'Share';
case ShareMenuTab.publish:
return LocaleKeys.shareAction_publish;
case ShareMenuTab.exportAs:
return 'Export as';
}
}
}
class ShareMenu extends StatefulWidget {
const ShareMenu({super.key});
@override
State<ShareMenu> createState() => _ShareMenuState();
}
class _ShareMenuState extends State<ShareMenu> {
ShareMenuTab selectedTab = ShareMenuTab.publish;
@override
Widget build(BuildContext context) {
final children = {
for (final tab in ShareMenuTab.supportedTabs)
tab: _Segment(
title: tab.i18n.tr(),
isSelected: selectedTab == tab,
),
};
return Column(
mainAxisSize: MainAxisSize.min,
children: [
CustomSlidingSegmentedControl<ShareMenuTab>(
initialValue: selectedTab,
curve: Curves.linear,
padding: 0,
innerPadding: const EdgeInsets.all(3.0),
children: children,
decoration: BoxDecoration(
color: const Color(0xFFEEF0F3),
borderRadius: BorderRadius.circular(8),
),
thumbDecoration: BoxDecoration(
color: Colors.white,
boxShadow: const [
BoxShadow(
color: Color(0x141F2225),
blurRadius: 8,
offset: Offset(0, 2),
),
],
borderRadius: BorderRadius.circular(8),
),
onValueChanged: (v) {
setState(() {
selectedTab = v;
});
},
),
_buildTab(context),
],
);
}
Widget _buildTab(BuildContext context) {
switch (selectedTab) {
case ShareMenuTab.publish:
return const PublishTab();
default:
return const Center(
child: FlowyText('coming soon'),
);
}
}
}
class _Segment extends StatelessWidget {
const _Segment({
required this.title,
required this.isSelected,
});
final String title;
final bool isSelected;
@override
Widget build(BuildContext context) {
final textColor = isSelected ? null : Theme.of(context).hintColor;
return SizedBox(
width: 128,
child: FlowyText(
title,
textAlign: TextAlign.center,
color: textColor,
),
);
}
}

View File

@ -13,6 +13,7 @@ class RoundedTextButton extends StatelessWidget {
final Color? hoverColor;
final Color? textColor;
final double? fontSize;
final FontWeight? fontWeight;
final EdgeInsets padding;
const RoundedTextButton({
@ -27,6 +28,7 @@ class RoundedTextButton extends StatelessWidget {
this.hoverColor,
this.textColor,
this.fontSize,
this.fontWeight,
this.padding = const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
});
@ -42,6 +44,7 @@ class RoundedTextButton extends StatelessWidget {
child: SizedBox.expand(
child: FlowyTextButton(
title ?? '',
fontWeight: fontWeight,
onPressed: onPressed,
fontSize: fontSize,
mainAxisAlignment: MainAxisAlignment.center,

View File

@ -394,6 +394,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.0"
custom_sliding_segmented_control:
dependency: "direct main"
description:
name: custom_sliding_segmented_control
sha256: "53c3e931c3ae1f696085d1ec70ac8e934da836595a9b7d9b88fdd0fcbf2a5574"
url: "https://pub.dev"
source: hosted
version: "1.8.3"
dart_style:
dependency: transitive
description:

View File

@ -149,6 +149,7 @@ dependencies:
bitsdojo_window: ^0.1.6
flutter_highlight: ^0.7.0
custom_sliding_segmented_control: ^1.8.3
dev_dependencies:
flutter_lints: ^3.0.1

View File

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 1.6875C13.0387 1.6875 16.3125 4.96125 16.3125 9C16.3125 13.0387 13.0387 16.3125 9 16.3125C4.96125 16.3125 1.6875 13.0387 1.6875 9C1.6875 4.96125 4.96125 1.6875 9 1.6875ZM8.4375 9.5625L6.1995 9.56288C6.21525 9.92288 6.24563 10.2731 6.2895 10.6117L6.32438 10.863L6.36787 11.1311C6.71512 13.1156 7.52325 14.607 8.43787 15.051L8.4375 9.5625ZM11.8005 9.56288L9.5625 9.5625V15.051C10.4565 14.6164 11.2489 13.1813 11.6081 11.2628L11.6321 11.1311L11.6756 10.8634C11.7402 10.4324 11.7819 9.99828 11.8005 9.56288ZM5.07337 9.56288H2.83763C3.04238 11.8316 4.47225 13.7479 6.4605 14.6441C6.07163 14.0415 5.751 13.3009 5.5155 12.4639L5.46225 12.2689L5.40038 12.0199L5.34337 11.7656C5.19117 11.0403 5.10084 10.3034 5.07337 9.56288ZM15.1624 9.56288H12.9262C12.9014 10.2245 12.827 10.8834 12.7035 11.5339L12.6562 11.7656L12.5992 12.0199L12.5374 12.2692C12.2974 13.1858 11.958 13.9954 11.5391 14.6441C13.5274 13.7479 14.9573 11.8316 15.1616 9.56288H15.1624ZM6.4605 3.3555L6.41025 3.37838C4.4475 4.28437 3.0405 6.1875 2.83763 8.4375H5.07337C5.10037 7.75125 5.1765 7.08938 5.29613 6.46613L5.34337 6.23438L5.40038 5.98012L5.46225 5.73075C5.70225 4.81425 6.04163 4.00463 6.4605 3.35588V3.3555ZM8.4375 2.949C7.54612 3.38175 6.75562 4.8105 6.39488 6.72113L6.36787 6.86888L6.32438 7.13662C6.25977 7.56774 6.21809 8.00197 6.1995 8.4375H8.4375V2.949ZM11.5395 3.35588L11.5845 3.42712C11.952 4.01475 12.2565 4.72762 12.4826 5.52863L12.5378 5.73112L12.5996 5.98012L12.6566 6.23438C12.8036 6.92438 12.8963 7.66538 12.9266 8.4375H15.1624C14.958 6.16838 13.5281 4.25213 11.5395 3.35625V3.35588ZM9.5625 2.949V8.4375H11.8005C11.7849 8.07668 11.7537 7.71672 11.7067 7.35862L11.6756 7.13662L11.6321 6.86888C11.2849 4.88475 10.4771 3.39338 9.56288 2.949H9.5625Z" fill="#171717"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -106,7 +106,12 @@
"html": "HTML",
"clipboard": "Copy to clipboard",
"csv": "CSV",
"copyLink": "Copy Link"
"copyLink": "Copy Link",
"publishToTheWeb": "Publish to the web",
"publishToTheWebHint": "Publish a static website of this page and control who see it",
"publish": "Publish",
"unPublish": "UnPublish",
"visitSite": "Visit site"
},
"moreAction": {
"small": "small",