fix: known issues in 0.6.7 (#5903)

* feat: add capitalize extension for string and apply it in emoji picker header and icon picker

* fix: adjust tooltip height calculation

* feat: add Streamline open source icons notice and link

* feat: enhance heading toolbar item to toggle heading level and cancel on same level selection

* feat: use home-3 as default space icon

* feat: use the first character of space name as icon if icon was removed

* chore: update hover effect for delete workspace button

* chore: optimize space icon on mobile

* fix: adjust chat ui on mobile

* fix: adjust default space icon on mobile
This commit is contained in:
Lucas.Xu 2024-08-08 19:31:10 +08:00 committed by GitHub
parent 7769034467
commit f57297e76d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 171 additions and 57 deletions

View File

@ -35,6 +35,8 @@ class MobileSpaceHeader extends StatelessWidget {
SpaceIcon(
dimension: 24,
space: space,
svgSize: 14,
textDimension: 18.0,
cornerRadius: 6.0,
),
const HSpace(8),

View File

@ -74,6 +74,8 @@ class _SidebarSpaceMenuItem extends StatelessWidget {
leftIcon: SpaceIcon(
dimension: 24,
space: space,
svgSize: 14,
textDimension: 18.0,
cornerRadius: 6.0,
),
leftIconSize: const Size.square(24),

View File

@ -1,8 +1,8 @@
import 'package:appflowy/plugins/ai_chat/application/chat_file_bloc.dart';
import 'package:appflowy/plugins/ai_chat/application/chat_input_action_bloc.dart';
import 'package:appflowy/plugins/ai_chat/application/chat_input_action_control.dart';
import 'package:appflowy/plugins/ai_chat/application/chat_input_bloc.dart';
import 'package:appflowy/plugins/ai_chat/presentation/chat_input_action_menu.dart';
import 'package:appflowy/plugins/ai_chat/application/chat_input_action_control.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mobile_page_selector_sheet.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/view/view_ext.dart';
@ -19,8 +19,8 @@ import 'package:flutter_chat_ui/flutter_chat_ui.dart';
import 'chat_at_button.dart';
import 'chat_attachment.dart';
import 'chat_send_button.dart';
import 'chat_input_span.dart';
import 'chat_send_button.dart';
class ChatInput extends StatefulWidget {
/// Creates [ChatInput] widget.
@ -64,7 +64,6 @@ class _ChatInputState extends State<ChatInput> {
_textController = InputTextFieldController();
_inputFocusNode = FocusNode(
onKeyEvent: (node, event) {
// TODO(lucas): support mobile
if (PlatformExtension.isDesktop) {
if (_inputActionControl.canHandleKeyEvent(event)) {
_inputActionControl.handleKeyEvent(event);
@ -122,7 +121,7 @@ class _ChatInputState extends State<ChatInput> {
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: _inputFocusNode.hasFocus
color: _inputFocusNode.hasFocus && !isMobile
? Theme.of(context).colorScheme.primary.withOpacity(0.6)
: Colors.transparent,
),
@ -141,8 +140,7 @@ class _ChatInputState extends State<ChatInput> {
_attachmentButton(buttonPadding),
Expanded(child: _inputTextField(context, textPadding)),
if (PlatformExtension.isDesktop &&
widget.aiType == const AIType.appflowyAI())
if (widget.aiType == const AIType.appflowyAI())
_atButton(buttonPadding),
_sendButton(buttonPadding),
],
@ -379,6 +377,7 @@ class _ChatInputState extends State<ChatInput> {
}
handler.onSelected(ViewActionPage(view: selectedView));
handler.onExit();
_inputFocusNode.requestFocus();
}
@override

View File

@ -1,3 +1,4 @@
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/string_extension.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
@ -20,7 +21,7 @@ class FlowyEmojiHeader extends StatelessWidget {
child: Padding(
padding: const EdgeInsets.only(bottom: 4.0),
child: FlowyText.regular(
category.id,
category.id.capitalize(),
color: Theme.of(context).hintColor,
),
),

View File

@ -1,5 +1,6 @@
extension Capitalize on String {
String capitalize() {
if (isEmpty) return this;
return "${this[0].toUpperCase()}${substring(1)}";
}
}

View File

@ -22,6 +22,7 @@ final headingsToolbarItem = ToolbarItem(
final node = editorState.getNodeAtPath(selection.start.path)!;
final delta = (node.delta ?? Delta()).toJson();
int level = node.attributes[HeadingBlockKeys.level] ?? 1;
final originLevel = level;
final isHighlight =
node.type == HeadingBlockKeys.type && (level >= 1 && level <= 3);
// only supports the level 1 - 3 in the toolbar, ignore the other levels
@ -53,13 +54,19 @@ final headingsToolbarItem = ToolbarItem(
currentLevel: isHighlight ? level : -1,
highlightColor: highlightColor,
child: child,
onLevelChanged: (level) async {
onLevelChanged: (newLevel) async {
// same level means cancel the heading
final type =
newLevel == originLevel && node.type == HeadingBlockKeys.type
? ParagraphBlockKeys.type
: HeadingBlockKeys.type;
await editorState.formatNode(
selection,
(node) => node.copyWith(
type: isHighlight ? ParagraphBlockKeys.type : HeadingBlockKeys.type,
type: type,
attributes: {
HeadingBlockKeys.level: level,
HeadingBlockKeys.level: newLevel,
blockComponentBackgroundColor:
node.attributes[blockComponentBackgroundColor],
blockComponentTextDirection:

View File

@ -1,7 +1,10 @@
import 'dart:convert';
import 'dart:math';
import 'package:appflowy/core/helpers/url_launcher.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/base/string_extension.dart';
import 'package:appflowy/shared/icon_emoji_picker/icon.dart';
import 'package:appflowy/shared/icon_emoji_picker/icon_search_bar.dart';
import 'package:appflowy/util/debounce.dart';
@ -9,7 +12,9 @@ import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_ic
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart' hide Icon;
import 'package:flutter/services.dart';
@ -198,7 +203,7 @@ class _IconPickerState extends State<IconPicker> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
FlowyText(
iconGroup.displayName,
iconGroup.displayName.capitalize(),
fontSize: 12,
figmaLineHeight: 18.0,
color: context.pickerTextColor,
@ -219,6 +224,10 @@ class _IconPickerState extends State<IconPicker> {
).toList(),
),
const VSpace(12.0),
if (index == widget.iconGroups.length - 1) ...[
const _StreamlinePermit(),
const VSpace(12.0),
],
],
);
},
@ -270,3 +279,39 @@ class _Icon extends StatelessWidget {
);
}
}
class _StreamlinePermit extends StatelessWidget {
const _StreamlinePermit();
@override
Widget build(BuildContext context) {
// Open source icons from Streamline
final textStyle = TextStyle(
fontSize: 12.0,
height: 18.0 / 12.0,
fontWeight: FontWeight.w500,
color: context.pickerTextColor,
);
return RichText(
text: TextSpan(
children: [
TextSpan(
text: '${LocaleKeys.emoji_openSourceIconsFrom.tr()} ',
style: textStyle,
),
TextSpan(
text: 'Streamline',
style: textStyle.copyWith(
decoration: TextDecoration.underline,
color: Theme.of(context).colorScheme.primary,
),
recognizer: TapGestureRecognizer()
..onTap = () {
afLaunchUrlString('https://www.streamlinehq.com/');
},
),
],
),
);
}
}

View File

@ -2,6 +2,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/_extension.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/shared_widget.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_icon.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_icon_popup.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
@ -17,7 +18,7 @@ class CreateSpacePopup extends StatefulWidget {
class _CreateSpacePopupState extends State<CreateSpacePopup> {
String spaceName = LocaleKeys.space_defaultSpaceName.tr();
String? spaceIcon = builtInSpaceIcons.first;
String? spaceIcon = kDefaultSpaceIconId;
String? spaceIconColor = builtInSpaceColors.first;
SpacePermission spacePermission = SpacePermission.publicToAll;
@ -47,6 +48,7 @@ class _CreateSpacePopupState extends State<CreateSpacePopup> {
SizedBox.square(
dimension: 56,
child: SpaceIconPopup(
onIconChanged: (icon, iconColor) {
spaceIcon = icon;
spaceIconColor = iconColor;

View File

@ -99,6 +99,7 @@ class _SpaceNameTextField extends StatelessWidget {
SizedBox.square(
dimension: 40,
child: SpaceIconPopup(
space: space,
cornerRadius: 12,
icon: space?.spaceIcon,
iconColor: space?.spaceIconColor,

View File

@ -496,17 +496,13 @@ class CurrentSpace extends StatelessWidget {
final child = Row(
mainAxisSize: MainAxisSize.min,
children: [
if (space.spaceIcon != null) ...[
SpaceIcon(
dimension: 22,
space: space,
svgSize: 12,
cornerRadius: 8.0,
),
const HSpace(10),
] else ...[
const HSpace(2),
],
SpaceIcon(
dimension: 22,
space: space,
svgSize: 12,
cornerRadius: 8.0,
),
const HSpace(10),
Flexible(
child: FlowyText.medium(
space.name,

View File

@ -87,6 +87,7 @@ class _SidebarSpaceMenuItem extends StatelessWidget {
leftIcon: SpaceIcon(
dimension: 20,
space: space,
svgSize: 12.0,
cornerRadius: 6.0,
),
leftIconSize: const Size.square(20),

View File

@ -1,35 +1,61 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/string_extension.dart';
import 'package:appflowy/shared/icon_emoji_picker/icon_picker.dart';
import 'package:appflowy/workspace/application/view/view_ext.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_icon_popup.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
class SpaceIcon extends StatelessWidget {
const SpaceIcon({
super.key,
required this.dimension,
this.textDimension,
this.cornerRadius = 0,
required this.space,
this.svgSize,
});
final double dimension;
final double? textDimension;
final double cornerRadius;
final ViewPB space;
final double? svgSize;
@override
Widget build(BuildContext context) {
final spaceIconColor = space.spaceIconColor;
final color = spaceIconColor != null
? Color(int.parse(spaceIconColor))
: Colors.transparent;
final svg = space.buildSpaceIconSvg(
context,
size: svgSize != null ? Size.square(svgSize!) : null,
);
if (svg == null) {
return const SizedBox.shrink();
// if space icon is null, use the first character of space name as icon
final Color color;
final Widget icon;
if (space.spaceIcon == null) {
final name = space.name.isNotEmpty ? space.name.capitalize()[0] : '';
icon = FlowyText.medium(
name,
color: Theme.of(context).colorScheme.surface,
fontSize: svgSize,
figmaLineHeight: textDimension ?? dimension,
);
color = Color(int.parse(builtInSpaceColors.first));
} else {
final spaceIconColor = space.spaceIconColor;
color = spaceIconColor != null
? Color(int.parse(spaceIconColor))
: Colors.transparent;
final svg = space.buildSpaceIconSvg(
context,
size: svgSize != null ? Size.square(svgSize!) : null,
);
if (svg == null) {
icon = const SizedBox.shrink();
} else {
icon =
svgSize == null || space.spaceIcon?.contains('space_icon') == true
? svg
: SizedBox.square(dimension: svgSize!, child: svg);
}
}
return ClipRRect(
@ -39,16 +65,15 @@ class SpaceIcon extends StatelessWidget {
height: dimension,
color: color,
child: Center(
child:
svgSize == null || space.spaceIcon?.contains('space_icon') == true
? svg
: SizedBox.square(dimension: svgSize!, child: svg),
child: icon,
),
),
);
}
}
const kDefaultSpaceIconId = 'interface_essential/home-3';
class DefaultSpaceIcon extends StatelessWidget {
const DefaultSpaceIcon({
super.key,
@ -63,7 +88,25 @@ class DefaultSpaceIcon extends StatelessWidget {
@override
Widget build(BuildContext context) {
final svg = builtInSpaceIcons.first;
final svgContent = kIconGroups?.findSvgContent(
kDefaultSpaceIconId,
);
final Widget svg;
if (svgContent != null) {
svg = FlowySvg.string(
svgContent,
size: Size.square(iconDimension),
color: Theme.of(context).colorScheme.surface,
);
} else {
svg = FlowySvg(
FlowySvgData('assets/flowy_icons/16x/${builtInSpaceIcons.first}.svg'),
color: Theme.of(context).colorScheme.surface,
size: Size.square(iconDimension),
);
}
final color = Color(int.parse(builtInSpaceColors.first));
return ClipRRect(
borderRadius: BorderRadius.circular(cornerRadius),
@ -71,10 +114,8 @@ class DefaultSpaceIcon extends StatelessWidget {
width: dimension,
height: dimension,
color: color,
child: FlowySvg(
FlowySvgData('assets/flowy_icons/16x/$svg.svg'),
color: Theme.of(context).colorScheme.surface,
size: Size.square(iconDimension),
child: Center(
child: svg,
),
),
);

View File

@ -6,6 +6,7 @@ import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
import 'package:appflowy/shared/icon_emoji_picker/icon_picker.dart';
import 'package:appflowy/shared/icon_emoji_picker/tab.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_icon.dart';
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_ui/flowy_infra_ui.dart';
@ -40,11 +41,13 @@ class SpaceIconPopup extends StatefulWidget {
this.icon,
this.iconColor,
this.cornerRadius = 16,
this.space,
required this.onIconChanged,
});
final String? icon;
final String? iconColor;
final ViewPB? space;
final void Function(String? icon, String? color) onIconChanged;
final double cornerRadius;
@ -114,11 +117,20 @@ class _SpaceIconPopupState extends State<SpaceIconPopup> {
builder: (_, value, __) {
Widget child;
if (value == null) {
child = const DefaultSpaceIcon(
cornerRadius: 16.0,
dimension: 32,
iconDimension: 32,
);
if (widget.space == null) {
child = DefaultSpaceIcon(
cornerRadius: widget.cornerRadius,
dimension: 32,
iconDimension: 32,
);
} else {
child = SpaceIcon(
dimension: 32,
space: widget.space!,
svgSize: 24,
cornerRadius: widget.cornerRadius,
);
}
} else if (value.contains('space_icon')) {
child = ClipRRect(
borderRadius: BorderRadius.circular(widget.cornerRadius),

View File

@ -108,7 +108,7 @@ class SpaceMoreActionTypeWrapper extends CustomActionCell {
) {
final child = _buildActionButton(context, null);
return AppFlowyPopover(
constraints: BoxConstraints.loose(const Size(380, 432)),
constraints: BoxConstraints.loose(const Size(360, 432)),
margin: const EdgeInsets.all(0),
clickHandler: PopoverClickHandler.gestureDetector,
offset: const Offset(0, -40),

View File

@ -85,14 +85,16 @@ class _WorkspaceMoreActionWrapper extends CustomActionCell {
BuildContext context,
PopoverController controller,
) {
return FlowyButton(
leftIcon: buildLeftIcon(context),
return FlowyIconTextButton(
leftIconBuilder: (onHover) => buildLeftIcon(context, onHover),
iconPadding: 10.0,
text: FlowyText.regular(
textBuilder: (onHover) => FlowyText.regular(
name,
fontSize: 14.0,
figmaLineHeight: 18.0,
color: [WorkspaceMoreAction.delete, WorkspaceMoreAction.leave]
.contains(inner)
.contains(inner) &&
onHover
? Theme.of(context).colorScheme.error
: null,
),
@ -161,12 +163,12 @@ class _WorkspaceMoreActionWrapper extends CustomActionCell {
}
}
Widget buildLeftIcon(BuildContext context) {
Widget buildLeftIcon(BuildContext context, bool onHover) {
switch (inner) {
case WorkspaceMoreAction.delete:
return FlowySvg(
FlowySvgs.delete_s,
color: Theme.of(context).colorScheme.error,
color: onHover ? Theme.of(context).colorScheme.error : null,
);
case WorkspaceMoreAction.rename:
return const FlowySvg(FlowySvgs.view_item_rename_s);

View File

@ -51,7 +51,7 @@ class FlowyTooltip extends StatelessWidget {
extension FlowyToolTipExtension on BuildContext {
double tooltipFontSize() => 14.0;
double tooltipHeight() => 18.0 / tooltipFontSize();
double tooltipHeight() => 20.0 / tooltipFontSize();
Color tooltipFontColor() => Theme.of(this).brightness == Brightness.light
? Colors.white
: Colors.black;
@ -62,6 +62,7 @@ extension FlowyToolTipExtension on BuildContext {
fontSize: tooltipFontSize(),
fontWeight: FontWeight.w400,
height: tooltipHeight(),
leadingDistribution: TextLeadingDistribution.even,
);
}

View File

@ -1910,7 +1910,8 @@
"medium": "Medium",
"mediumDark": "Medium-Dark",
"dark": "Dark"
}
},
"openSourceIconsFrom": "Open source icons from"
},
"inlineActions": {
"noResults": "No results",
@ -2378,4 +2379,4 @@
"commentAddedSuccessfully": "Comment added successfully.",
"commentAddedSuccessTip": "You've just added or replied to a comment. Would you like to jump to the top to see the latest comments?"
}
}
}