fix: v0.6.2 issues (#5654)

* fix: remove create button in move to menu

* fix: add loading indicator when duplicating space

* fix: sidebar header expand icon status

* fix: text within select tag overflow

* fix: callout block icon align issue

* feat: sync the space icon when creating a space

* fix: duplicated hover views

* fix: cover image doesn't update
This commit is contained in:
Lucas.Xu 2024-07-01 14:43:57 +08:00 committed by GitHub
parent 50f5be3e75
commit c78f23e1c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 143 additions and 99 deletions

View File

@ -1,8 +1,8 @@
import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
import 'package:appflowy/plugins/database/widgets/cell_editor/extension.dart';
import 'package:appflowy/plugins/database/application/cell/bloc/select_option_cell_bloc.dart';
import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
import 'package:appflowy/plugins/database/widgets/cell_editor/extension.dart';
import 'package:appflowy/plugins/database/widgets/cell_editor/select_option_cell_editor.dart';
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
@ -56,7 +56,7 @@ class DesktopGridSelectOptionCellSkin extends IEditableSelectOptionCellSkin {
child: SelectOptionTag(
option: option,
padding: const EdgeInsets.symmetric(
vertical: 1,
vertical: 4,
horizontal: 8,
),
),

View File

@ -1,8 +1,8 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
import 'package:appflowy/plugins/database/widgets/cell_editor/extension.dart';
import 'package:appflowy/plugins/database/application/cell/bloc/select_option_cell_bloc.dart';
import 'package:appflowy/plugins/database/widgets/cell_editor/extension.dart';
import 'package:appflowy/plugins/database/widgets/cell_editor/select_option_cell_editor.dart';
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
@ -67,7 +67,7 @@ class DesktopRowDetailSelectOptionCellSkin
return SelectOptionTag(
option: option,
padding: const EdgeInsets.symmetric(
vertical: 1,
vertical: 4,
horizontal: 8,
),
);

View File

@ -96,7 +96,6 @@ class SelectOptionTag extends StatelessWidget {
);
return Container(
height: 20,
padding: onRemove == null ? padding : padding.copyWith(right: 2.0),
decoration: BoxDecoration(
color: optionColor,

View File

@ -1,10 +1,6 @@
import 'dart:collection';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database/application/cell/bloc/select_option_cell_editor_bloc.dart';
@ -14,12 +10,14 @@ import 'package:appflowy_popover/appflowy_popover.dart';
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:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../grid/presentation/layout/sizes.dart';
import '../../grid/presentation/widgets/common/type_option_separator.dart';
import '../field/type_option_editor/select/select_option_editor.dart';
import 'extension.dart';
import 'select_option_text_field.dart';
@ -476,11 +474,11 @@ class SelectOptionTagCell extends StatelessWidget {
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 6.0,
vertical: 4.0,
),
child: SelectOptionTag(
option: option,
padding: const EdgeInsets.symmetric(horizontal: 8),
padding:
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
),
),
),

View File

@ -132,7 +132,10 @@ class _SelectOptionTextFieldState extends State<SelectOptionTextField> {
(option) => SelectOptionTag(
option: option,
onRemove: (option) => widget.onRemove(option),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 1),
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
),
)
.toList();

View File

@ -8,7 +8,6 @@ import 'package:appflowy/plugins/document/application/document_data_pb_extension
import 'package:appflowy/plugins/document/application/document_listener.dart';
import 'package:appflowy/plugins/document/application/document_service.dart';
import 'package:appflowy/plugins/document/application/editor_transaction_adapter.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/migration/editor_migration.dart';
import 'package:appflowy/plugins/trash/application/trash_service.dart';
import 'package:appflowy/shared/feature_flags.dart';
import 'package:appflowy/startup/startup.dart';
@ -19,7 +18,6 @@ import 'package:appflowy/util/color_to_hex_string.dart';
import 'package:appflowy/util/debounce.dart';
import 'package:appflowy/util/throttle.dart';
import 'package:appflowy/workspace/application/view/view_listener.dart';
import 'package:appflowy/workspace/application/view/view_service.dart';
import 'package:appflowy_backend/protobuf/flowy-document/entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-document/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
@ -118,11 +116,6 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
final result = await _fetchDocumentState();
_onViewChanged();
_onDocumentChanged();
result.onSuccess((s) {
if (s != null) {
_migrateCover(s);
}
});
final newState = await result.fold(
(s) async {
final userProfilePB =
@ -390,14 +383,6 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
metadata: jsonEncode(metadata.toJson()),
);
}
// from version 0.5.5, the cover is stored in the view.ext
Future<void> _migrateCover(EditorState editorState) async {
final view = await ViewBackendService.getView(documentId);
view.onSuccess((s) {
return EditorMigration.migrateCoverIfNeeded(s, editorState);
});
}
}
@freezed

View File

@ -196,6 +196,7 @@ class _CalloutBlockComponentWidgetState
), // force to refresh the popover state
title: '',
emoji: emoji,
emojiSize: 16.0,
onSubmitted: (emoji, controller) {
setEmoji(emoji);
controller?.close();
@ -204,7 +205,7 @@ class _CalloutBlockComponentWidgetState
),
Flexible(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
padding: const EdgeInsets.symmetric(vertical: 6.0),
child: buildCalloutBlockComponent(context, textDirection),
),
),

View File

@ -91,6 +91,7 @@ class _DocumentCoverWidgetState extends State<DocumentCoverWidget> {
String viewIcon = '';
PageStyleCover? cover;
late ViewPB view;
late final ViewListener viewListener;
@override
@ -99,6 +100,7 @@ class _DocumentCoverWidgetState extends State<DocumentCoverWidget> {
final value = widget.view.icon.value;
viewIcon = value.isNotEmpty ? value : icon ?? '';
cover = widget.view.cover;
view = widget.view;
widget.node.addListener(_reload);
viewListener = ViewListener(
viewId: widget.view.id,
@ -107,6 +109,7 @@ class _DocumentCoverWidgetState extends State<DocumentCoverWidget> {
setState(() {
viewIcon = p0.icon.value;
cover = p0.cover;
view = p0;
});
},
);
@ -137,7 +140,7 @@ class _DocumentCoverWidgetState extends State<DocumentCoverWidget> {
),
if (hasCover)
DocumentCover(
view: widget.view,
view: view,
editorState: widget.editorState,
node: widget.node,
coverType: coverType,
@ -204,7 +207,7 @@ class _DocumentCoverWidgetState extends State<DocumentCoverWidget> {
// compatible with version > 0.5.5.
EditorMigration.migrateCoverIfNeeded(
widget.view,
widget.editorState,
attributes,
overwrite: true,
);
}

View File

@ -164,18 +164,17 @@ class EditorMigration {
// Now, the cover is stored in the view.ext.
static void migrateCoverIfNeeded(
ViewPB view,
EditorState editorState, {
Attributes attributes, {
bool overwrite = false,
}) async {
if (view.extra.isNotEmpty && !overwrite) {
return;
}
final root = editorState.document.root;
final coverType = CoverType.fromString(
root.attributes[DocumentHeaderBlockKeys.coverType],
attributes[DocumentHeaderBlockKeys.coverType],
);
final coverDetails = root.attributes[DocumentHeaderBlockKeys.coverDetails];
final coverDetails = attributes[DocumentHeaderBlockKeys.coverDetails];
Map extra = {};

View File

@ -297,11 +297,16 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
if (currentSpace == null) {
return;
}
emit(state.copyWith(isDuplicatingSpace: true));
final newSpace = await _duplicateSpace(currentSpace);
// open the duplicated space
if (newSpace != null) {
add(const SpaceEvent.didReceiveSpaceUpdate());
add(SpaceEvent.open(newSpace));
}
emit(state.copyWith(isDuplicatingSpace: false));
},
);
},
@ -346,25 +351,22 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
SpacePermission.private => ViewSectionPB.Private,
};
final extra = {
ViewExtKeys.isSpaceKey: true,
ViewExtKeys.spaceIconKey: icon,
ViewExtKeys.spaceIconColorKey: iconColor,
ViewExtKeys.spacePermissionKey: permission.index,
ViewExtKeys.spaceCreatedAtKey: DateTime.now().millisecondsSinceEpoch,
};
final result = await _workspaceService.createView(
name: name,
viewSection: section,
setAsCurrent: false,
setAsCurrent: true,
viewId: viewId,
extra: jsonEncode(extra),
);
return await result.fold((space) async {
Log.info('Space created: $space');
final extra = {
ViewExtKeys.isSpaceKey: true,
ViewExtKeys.spaceIconKey: icon,
ViewExtKeys.spaceIconColorKey: iconColor,
ViewExtKeys.spacePermissionKey: permission.index,
ViewExtKeys.spaceCreatedAtKey: DateTime.now().millisecondsSinceEpoch,
};
await ViewBackendService.updateView(
viewId: space.id,
extra: jsonEncode(extra),
);
return space;
}, (error) {
Log.error('Failed to create space: $error');
@ -620,19 +622,17 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
}
for (final view in space.childViews) {
unawaited(
ViewBackendService.duplicate(
view: view,
openAfterDuplicate: true,
includeChildren: true,
parentViewId: newSpace.id,
suffix: '',
),
await ViewBackendService.duplicate(
view: view,
openAfterDuplicate: true,
includeChildren: true,
parentViewId: newSpace.id,
suffix: '',
);
}
Log.info('Space duplicated: $newSpace');
add(const SpaceEvent.didReceiveSpaceUpdate());
return newSpace;
}
}
@ -688,6 +688,7 @@ class SpaceState with _$SpaceState {
@Default(null) ViewPB? lastCreatedPage,
FlowyResult<void, FlowyError>? createPageResult,
@Default(false) bool shouldShowUpgradeDialog,
@Default(false) bool isDuplicatingSpace,
}) = _SpaceState;
factory SpaceState.initial() => const SpaceState();

View File

@ -133,13 +133,13 @@ extension ViewExtension on ViewPB {
}
}
FlowySvg get spaceIconSvg {
FlowySvg? get spaceIconSvg {
try {
final ext = jsonDecode(extra);
final icon = ext[ViewExtKeys.spaceIconKey];
final color = ext[ViewExtKeys.spaceIconColorKey];
if (icon == null || color == null) {
return const FlowySvg(FlowySvgs.space_icon_s, blendMode: null);
return null;
}
return FlowySvg(
FlowySvgData('assets/flowy_icons/16x/$icon.svg'),
@ -147,7 +147,7 @@ extension ViewExtension on ViewPB {
blendMode: BlendMode.srcOut,
);
} catch (e) {
return const FlowySvg(FlowySvgs.space_icon_s, blendMode: null);
return null;
}
}

View File

@ -19,6 +19,7 @@ class WorkspaceService {
ViewLayoutPB? layout,
bool? setAsCurrent,
String? viewId,
String? extra,
}) {
final payload = CreateViewPayloadPB.create()
..parentViewId = workspaceId
@ -42,6 +43,10 @@ class WorkspaceService {
payload.viewId = viewId;
}
if (extra != null) {
payload.extra = extra;
}
return FolderEventCreateView(payload).send();
}

View File

@ -117,6 +117,7 @@ class _MovePageMenuState extends State<MovePageMenu> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SpacePopup(
showCreateButton: false,
child: CurrentSpace(
space: space,
),

View File

@ -5,6 +5,7 @@ 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/blank/blank.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/widgets/loading.dart';
import 'package:appflowy/shared/feature_flags.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/action_navigation/action_navigation_bloc.dart';
@ -38,6 +39,8 @@ import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
Loading? _duplicateSpaceLoading;
/// Home Sidebar is the left side bar of the home page.
///
/// in the sidebar, we have:
@ -136,7 +139,8 @@ class HomeSideBar extends StatelessWidget {
),
BlocListener<SpaceBloc, SpaceState>(
listenWhen: (p, c) =>
p.lastCreatedPage?.id != c.lastCreatedPage?.id,
p.lastCreatedPage?.id != c.lastCreatedPage?.id ||
p.isDuplicatingSpace != c.isDuplicatingSpace,
listener: (context, state) {
final page = state.lastCreatedPage;
if (page == null || page.id.isEmpty) {
@ -153,6 +157,14 @@ class HomeSideBar extends StatelessWidget {
),
);
}
if (state.isDuplicatingSpace) {
_duplicateSpaceLoading ??= Loading(context);
_duplicateSpaceLoading?.start();
} else if (_duplicateSpaceLoading != null) {
_duplicateSpaceLoading?.stop();
_duplicateSpaceLoading = null;
}
},
),
BlocListener<ActionNavigationBloc, ActionNavigationState>(

View File

@ -277,9 +277,11 @@ class DeleteSpacePopup extends StatelessWidget {
class SpacePopup extends StatelessWidget {
const SpacePopup({
super.key,
required this.showCreateButton,
required this.child,
});
final bool showCreateButton;
final Widget child;
@override
@ -293,7 +295,9 @@ class SpacePopup extends StatelessWidget {
offset: const Offset(0, 4),
popupBuilder: (_) => BlocProvider.value(
value: context.read<SpaceBloc>(),
child: const SidebarSpaceMenu(),
child: SidebarSpaceMenu(
showCreateButton: showCreateButton,
),
),
child: FlowyButton(
useIntrinsicWidth: true,
@ -333,8 +337,10 @@ class CurrentSpace extends StatelessWidget {
),
),
const HSpace(4.0),
const FlowySvg(
FlowySvgs.workspace_drop_down_menu_show_s,
FlowySvg(
context.read<SpaceBloc>().state.isExpanded
? FlowySvgs.workspace_drop_down_menu_show_s
: FlowySvgs.workspace_drop_down_menu_hide_s,
),
],
);

View File

@ -89,6 +89,7 @@ class _SidebarSpaceHeaderState extends State<SidebarSpaceHeader> {
bottom: 3,
right: isHovered.value || onEditing ? 88 : 0,
child: SpacePopup(
showCreateButton: true,
child: _buildChild(),
),
),

View File

@ -14,7 +14,12 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class SidebarSpaceMenu extends StatelessWidget {
const SidebarSpaceMenu({super.key});
const SidebarSpaceMenu({
super.key,
required this.showCreateButton,
});
final bool showCreateButton;
@override
Widget build(BuildContext context) {
@ -32,16 +37,18 @@ class SidebarSpaceMenu extends StatelessWidget {
isSelected: state.currentSpace?.id == space.id,
),
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 8.0),
child: Divider(
height: 0.5,
if (showCreateButton) ...[
const Padding(
padding: EdgeInsets.symmetric(vertical: 8.0),
child: Divider(
height: 0.5,
),
),
),
const SizedBox(
height: HomeSpaceViewSizes.viewHeight,
child: _CreateSpaceButton(),
),
const SizedBox(
height: HomeSpaceViewSizes.viewHeight,
child: _CreateSpaceButton(),
),
],
],
);
},

View File

@ -257,26 +257,33 @@ class _InnerViewItemState extends State<InnerViewItem> {
@override
Widget build(BuildContext context) {
Widget child = SingleInnerViewItem(
view: widget.view,
parentView: widget.parentView,
level: widget.level,
showActions: widget.showActions,
spaceType: widget.spaceType,
onSelected: widget.onSelected,
onTertiarySelected: widget.onTertiarySelected,
isExpanded: widget.isExpanded,
isDraggable: widget.isDraggable,
leftPadding: widget.leftPadding,
isFeedback: widget.isFeedback,
height: widget.height,
isPlaceholder: widget.isPlaceholder,
isHovered: widget.isHovered,
leftIconBuilder: widget.leftIconBuilder,
rightIconsBuilder: widget.rightIconsBuilder,
extendBuilder: widget.extendBuilder,
disableSelectedStatus: widget.disableSelectedStatus,
shouldIgnoreView: widget.shouldIgnoreView,
Widget child = ValueListenableBuilder(
valueListenable: getIt<MenuSharedState>().notifier,
builder: (context, value, _) {
final isSelected = value?.id == widget.view.id;
return SingleInnerViewItem(
view: widget.view,
parentView: widget.parentView,
level: widget.level,
showActions: widget.showActions,
spaceType: widget.spaceType,
onSelected: widget.onSelected,
onTertiarySelected: widget.onTertiarySelected,
isExpanded: widget.isExpanded,
isDraggable: widget.isDraggable,
leftPadding: widget.leftPadding,
isFeedback: widget.isFeedback,
height: widget.height,
isPlaceholder: widget.isPlaceholder,
isHovered: widget.isHovered,
leftIconBuilder: widget.leftIconBuilder,
rightIconsBuilder: widget.rightIconsBuilder,
extendBuilder: widget.extendBuilder,
disableSelectedStatus: widget.disableSelectedStatus,
shouldIgnoreView: widget.shouldIgnoreView,
isSelected: isSelected,
);
},
);
// if the view is expanded and has child views, render its child views
@ -403,6 +410,7 @@ class SingleInnerViewItem extends StatefulWidget {
required this.extendBuilder,
required this.disableSelectedStatus,
required this.shouldIgnoreView,
required this.isSelected,
});
final ViewPB view;
@ -430,6 +438,7 @@ class SingleInnerViewItem extends StatefulWidget {
final List<Widget> Function(ViewPB view)? extendBuilder;
final bool Function(ViewPB view)? shouldIgnoreView;
final bool isSelected;
@override
State<SingleInnerViewItem> createState() => _SingleInnerViewItemState();
@ -441,8 +450,8 @@ class _SingleInnerViewItemState extends State<SingleInnerViewItem> {
@override
Widget build(BuildContext context) {
var isSelected =
getIt<MenuSharedState>().latestOpenView?.id == widget.view.id;
var isSelected = widget.isSelected;
if (widget.disableSelectedStatus == true) {
isSelected = false;
}

View File

@ -22,6 +22,7 @@ impl EventIntegrationTest {
index: None,
section: None,
view_id: None,
extra: None,
};
EventBuilder::new(self.clone())
.event(FolderEvent::CreateView)

View File

@ -46,6 +46,7 @@ impl EventIntegrationTest {
index: None,
section: None,
view_id: None,
extra: None,
};
EventBuilder::new(self.clone())
.event(FolderEvent::CreateView)
@ -78,6 +79,7 @@ impl EventIntegrationTest {
index: None,
section: None,
view_id: None,
extra: None,
};
EventBuilder::new(self.clone())
.event(FolderEvent::CreateView)
@ -105,6 +107,7 @@ impl EventIntegrationTest {
index: None,
section: None,
view_id: None,
extra: None,
};
EventBuilder::new(self.clone())
.event(FolderEvent::CreateView)

View File

@ -66,6 +66,7 @@ impl DocumentEventTest {
index: None,
section: None,
view_id: None,
extra: None,
};
EventBuilder::new(core.clone())
.event(FolderEvent::CreateView)

View File

@ -43,6 +43,7 @@ impl EventIntegrationTest {
index: None,
section: None,
view_id: None,
extra: None,
};
let view = EventBuilder::new(self.clone())
.event(FolderEvent::CreateView)

View File

@ -238,6 +238,7 @@ impl EventIntegrationTest {
index: None,
section: None,
view_id: None,
extra: None,
};
EventBuilder::new(self.clone())
.event(FolderEvent::CreateView)
@ -302,6 +303,7 @@ impl ViewTest {
index: None,
section: None,
view_id: None,
extra: None,
};
let view = EventBuilder::new(sdk.clone())

View File

@ -253,6 +253,7 @@ pub async fn create_view(
index: None,
section: None,
view_id: None,
extra: None,
};
EventBuilder::new(sdk.clone())
.event(CreateView)

View File

@ -261,6 +261,11 @@ pub struct CreateViewPayloadPB {
#[pb(index = 11, one_of)]
pub view_id: Option<String>,
// The extra data of the view.
// Refer to the extra field in the collab
#[pb(index = 12, one_of)]
pub extra: Option<String>,
}
#[derive(Eq, PartialEq, Hash, Debug, ProtoBuf_Enum, Clone, Default)]
@ -335,7 +340,7 @@ impl TryInto<CreateViewParams> for CreateViewPayloadPB {
index: self.index,
section: self.section,
icon: None,
extra: None,
extra: self.extra,
})
}
}