mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge branch 'AppFlowy-IO:main' into main
This commit is contained in:
commit
e4c15f2eff
@ -1,7 +1,7 @@
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_page_block.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/sync_error_page.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
import 'package:flowy_infra/uuid.dart';
|
||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
@ -92,7 +92,7 @@ void main() {
|
||||
);
|
||||
expect(finder, findsOneWidget);
|
||||
await tester.tapButton(finder);
|
||||
expect(find.byType(FlowyErrorPage), findsOneWidget);
|
||||
expect(find.byType(SyncErrorPage), findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/gesture.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/animated_gesture.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||
import 'package:appflowy/mobile/presentation/home/workspaces/workspace_menu_bottom_sheet.dart';
|
||||
import 'package:appflowy/plugins/base/emoji/emoji_picker_screen.dart';
|
||||
@ -109,6 +109,7 @@ class _MobileWorkspace extends StatelessWidget {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return AnimatedGestureDetector(
|
||||
scaleFactor: 0.99,
|
||||
alignment: Alignment.centerLeft,
|
||||
onTapUp: () {
|
||||
context.read<UserWorkspaceBloc>().add(
|
||||
|
@ -5,7 +5,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||
import 'package:appflowy/mobile/application/page_style/document_page_style_bloc.dart';
|
||||
import 'package:appflowy/mobile/application/recent/recent_view_bloc.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/gesture.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/animated_gesture.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||
import 'package:appflowy/shared/appflowy_network_image.dart';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/gesture.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/animated_gesture.dart';
|
||||
import 'package:appflowy/mobile/presentation/home/tab/mobile_space_tab.dart';
|
||||
import 'package:appflowy/util/theme_extension.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import 'package:appflowy/mobile/application/notification/notification_reminder_bloc.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/gesture.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/animated_gesture.dart';
|
||||
import 'package:appflowy/mobile/presentation/notifications/mobile_notifications_screen.dart';
|
||||
import 'package:appflowy/mobile/presentation/notifications/widgets/widgets.dart';
|
||||
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||
import 'package:appflowy/mobile/application/notification/notification_reminder_bloc.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/gesture.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/animated_gesture.dart';
|
||||
import 'package:appflowy/mobile/presentation/notifications/widgets/widgets.dart';
|
||||
import 'package:appflowy/user/application/reminder/reminder_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
||||
|
@ -55,6 +55,8 @@ class RowCache {
|
||||
final RowLifeCycle _rowLifeCycle;
|
||||
final RowFieldsDelegate _fieldDelegate;
|
||||
RowChangesetNotifier? _changedNotifier;
|
||||
bool _isInitialRows = false;
|
||||
final List<RowsVisibilityChangePB> _pendingVisibilityChanges = [];
|
||||
|
||||
/// Returns a unmodifiable list of RowInfo
|
||||
UnmodifiableListView<RowInfo> get rowInfos {
|
||||
@ -80,7 +82,13 @@ class RowCache {
|
||||
final rowInfo = buildGridRow(row);
|
||||
_rowList.add(rowInfo);
|
||||
}
|
||||
_isInitialRows = true;
|
||||
_changedNotifier?.receive(const ChangedReason.setInitialRows());
|
||||
|
||||
for (final changeset in _pendingVisibilityChanges) {
|
||||
applyRowsVisibility(changeset);
|
||||
}
|
||||
_pendingVisibilityChanges.clear();
|
||||
}
|
||||
|
||||
void setRowMeta(RowMetaPB rowMeta) {
|
||||
@ -103,8 +111,12 @@ class RowCache {
|
||||
}
|
||||
|
||||
void applyRowsVisibility(RowsVisibilityChangePB changeset) {
|
||||
_hideRows(changeset.invisibleRows);
|
||||
_showRows(changeset.visibleRows);
|
||||
if (_isInitialRows) {
|
||||
_hideRows(changeset.invisibleRows);
|
||||
_showRows(changeset.visibleRows);
|
||||
} else {
|
||||
_pendingVisibilityChanges.add(changeset);
|
||||
}
|
||||
}
|
||||
|
||||
void reorderAllRows(List<String> rowIds) {
|
||||
|
@ -202,11 +202,18 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
);
|
||||
},
|
||||
endEditingHeader: (String groupId, String? groupName) async {
|
||||
await groupBackendSvc.updateGroup(
|
||||
fieldId: groupControllers.values.first.group.fieldId,
|
||||
groupId: groupId,
|
||||
name: groupName,
|
||||
);
|
||||
final group = groupControllers[groupId]?.group;
|
||||
if (group != null) {
|
||||
if (generateGroupNameFromGroup(group) != groupName) {
|
||||
await groupBackendSvc.updateGroup(
|
||||
fieldId: groupControllers.values.first.group.fieldId,
|
||||
groupId: groupId,
|
||||
name: groupName,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
state.maybeMap(
|
||||
ready: (state) => emit(state.copyWith(editingHeaderId: null)),
|
||||
orElse: () {},
|
||||
|
@ -556,10 +556,8 @@ class _BoardCardState extends State<_BoardCard> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final boardBloc = context.read<BoardBloc>();
|
||||
|
||||
final groupData = widget.afGroupData.customData as GroupData;
|
||||
final rowCache = boardBloc.rowCache;
|
||||
|
||||
final databaseController = boardBloc.databaseController;
|
||||
final rowMeta =
|
||||
rowCache.getRow(widget.groupItem.id)?.rowMeta ?? widget.groupItem.row;
|
||||
|
@ -42,6 +42,8 @@ class HiddenGroupsColumn extends StatelessWidget {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
final isCollapsed = layoutSettings.collapseHiddenGroups;
|
||||
final leftPadding = margin.left +
|
||||
context.read<DatabasePluginWidgetBuilderSize>().horizontalPadding;
|
||||
return AnimatedSize(
|
||||
alignment: AlignmentDirectional.topStart,
|
||||
curve: Curves.easeOut,
|
||||
@ -56,35 +58,29 @@ class HiddenGroupsColumn extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
)
|
||||
: SizedBox(
|
||||
: Container(
|
||||
width: 274,
|
||||
padding: EdgeInsets.only(
|
||||
left: leftPadding,
|
||||
right: margin.right + 4,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 50,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: margin.left +
|
||||
context
|
||||
.read<DatabasePluginWidgetBuilderSize>()
|
||||
.horizontalPadding,
|
||||
right: margin.right + 4,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: FlowyText.medium(
|
||||
LocaleKeys
|
||||
.board_hiddenGroupSection_sectionTitle
|
||||
.tr(),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: FlowyText.medium(
|
||||
LocaleKeys.board_hiddenGroupSection_sectionTitle
|
||||
.tr(),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
_collapseExpandIcon(context, isCollapsed),
|
||||
],
|
||||
),
|
||||
),
|
||||
_collapseExpandIcon(context, isCollapsed),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
@ -204,31 +200,27 @@ class _HiddenGroupCardState extends State<HiddenGroupCard> {
|
||||
final databaseController = widget.bloc.databaseController;
|
||||
final primaryField = databaseController.fieldController.fieldInfos
|
||||
.firstWhereOrNull((element) => element.isPrimary)!;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 26),
|
||||
child: AppFlowyPopover(
|
||||
controller: _popoverController,
|
||||
direction: PopoverDirection.bottomWithCenterAligned,
|
||||
triggerActions: PopoverTriggerFlags.none,
|
||||
constraints: const BoxConstraints(maxWidth: 234, maxHeight: 300),
|
||||
popupBuilder: (popoverContext) {
|
||||
return BlocProvider.value(
|
||||
value: context.read<BoardBloc>(),
|
||||
child: HiddenGroupPopupItemList(
|
||||
viewId: databaseController.viewId,
|
||||
groupId: widget.group.groupId,
|
||||
primaryFieldId: primaryField.id,
|
||||
rowCache: databaseController.rowCache,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: HiddenGroupButtonContent(
|
||||
popoverController: _popoverController,
|
||||
groupId: widget.group.groupId,
|
||||
index: widget.index,
|
||||
bloc: widget.bloc,
|
||||
),
|
||||
return AppFlowyPopover(
|
||||
controller: _popoverController,
|
||||
direction: PopoverDirection.bottomWithCenterAligned,
|
||||
triggerActions: PopoverTriggerFlags.none,
|
||||
constraints: const BoxConstraints(maxWidth: 234, maxHeight: 300),
|
||||
popupBuilder: (popoverContext) {
|
||||
return BlocProvider.value(
|
||||
value: context.read<BoardBloc>(),
|
||||
child: HiddenGroupPopupItemList(
|
||||
viewId: databaseController.viewId,
|
||||
groupId: widget.group.groupId,
|
||||
primaryFieldId: primaryField.id,
|
||||
rowCache: databaseController.rowCache,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: HiddenGroupButtonContent(
|
||||
popoverController: _popoverController,
|
||||
groupId: widget.group.groupId,
|
||||
index: widget.index,
|
||||
bloc: widget.bloc,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/application/page_style/document_page_style_bloc.dart';
|
||||
import 'package:appflowy/plugins/document/application/document_bloc.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/banner.dart';
|
||||
@ -12,6 +11,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/image/cust
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/multi_image_block_component/multi_image_block_component.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/sync_error_page.dart';
|
||||
import 'package:appflowy/shared/patterns/common_patterns.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/application/action_navigation/action_navigation_bloc.dart';
|
||||
@ -22,8 +22,6 @@ import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
||||
import 'package:cross_file/cross_file.dart';
|
||||
import 'package:desktop_drop/desktop_drop.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
@ -115,9 +113,10 @@ class _DocumentPageState extends State<DocumentPage>
|
||||
final error = state.error;
|
||||
if (error != null || editorState == null) {
|
||||
Log.error(error);
|
||||
return FlowyErrorPage.message(
|
||||
error.toString(),
|
||||
howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),
|
||||
return Center(
|
||||
child: SyncErrorPage(
|
||||
error: error,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -500,7 +500,7 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
|
||||
void _customizeBlockComponentBackgroundColorDecorator() {
|
||||
blockComponentBackgroundColorDecorator = (Node node, String colorString) {
|
||||
if (context.mounted) {
|
||||
if (mounted && context.mounted) {
|
||||
return buildEditorCustomizedColor(context, node, colorString);
|
||||
}
|
||||
return null;
|
||||
|
@ -1,7 +1,6 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||
import 'package:appflowy/plugins/base/emoji/emoji_text.dart';
|
||||
import 'package:appflowy/plugins/document/application/document_bloc.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_block.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mobile_page_selector_sheet.dart';
|
||||
@ -106,70 +105,32 @@ class _MentionPageBlockState extends State<MentionPageBlock> {
|
||||
final view = state.data;
|
||||
// memorize the result
|
||||
pageMemorizer[widget.pageId] = view;
|
||||
|
||||
if (view == null) {
|
||||
return FlowyHover(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||
child: FlowyText(
|
||||
LocaleKeys.document_mention_noAccess.tr(),
|
||||
color: Theme.of(context).disabledColor,
|
||||
decoration: TextDecoration.underline,
|
||||
fontSize: widget.textStyle?.fontSize,
|
||||
fontWeight: widget.textStyle?.fontWeight,
|
||||
),
|
||||
),
|
||||
return _NoAccessMentionPageBlock(
|
||||
textStyle: widget.textStyle,
|
||||
);
|
||||
}
|
||||
|
||||
final iconSize = widget.textStyle?.fontSize ?? 16.0;
|
||||
Widget child = Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const HSpace(4),
|
||||
view.icon.value.isNotEmpty
|
||||
? EmojiText(
|
||||
emoji: view.icon.value,
|
||||
fontSize: 12,
|
||||
textAlign: TextAlign.center,
|
||||
lineHeight: 1.3,
|
||||
)
|
||||
: FlowySvg(
|
||||
view.layout.icon,
|
||||
size: Size.square(iconSize + 2.0),
|
||||
),
|
||||
const HSpace(2),
|
||||
FlowyText(
|
||||
view.name,
|
||||
decoration: TextDecoration.underline,
|
||||
fontSize: widget.textStyle?.fontSize,
|
||||
fontWeight: widget.textStyle?.fontWeight,
|
||||
),
|
||||
const HSpace(4),
|
||||
],
|
||||
);
|
||||
|
||||
if (PlatformExtension.isDesktop) {
|
||||
child = Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 2),
|
||||
child: FlowyHover(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: child,
|
||||
),
|
||||
if (PlatformExtension.isMobile) {
|
||||
return _MobileMentionPageBlock(
|
||||
view: view,
|
||||
textStyle: widget.textStyle,
|
||||
handleTap: handleTap,
|
||||
handleDoubleTap: handleDoubleTap,
|
||||
);
|
||||
} else {
|
||||
return _DesktopMentionPageBlock(
|
||||
view: view,
|
||||
textStyle: widget.textStyle,
|
||||
handleTap: handleTap,
|
||||
);
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
onTap: handleTap,
|
||||
onDoubleTap: PlatformExtension.isMobile ? handleDoubleTap : null,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> handleTap() async {
|
||||
debugPrint('handleTap');
|
||||
final view = await fetchView(widget.pageId);
|
||||
if (view == null) {
|
||||
Log.error('Page(${widget.pageId}) not found');
|
||||
@ -246,3 +207,127 @@ class _MentionPageBlockState extends State<MentionPageBlock> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class _MentionPageBlockContent extends StatelessWidget {
|
||||
const _MentionPageBlockContent({
|
||||
required this.view,
|
||||
required this.textStyle,
|
||||
});
|
||||
|
||||
final ViewPB view;
|
||||
final TextStyle? textStyle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final emojiSize = textStyle?.fontSize ?? 12.0;
|
||||
final iconSize = textStyle?.fontSize ?? 16.0;
|
||||
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const HSpace(4),
|
||||
view.icon.value.isNotEmpty
|
||||
? FlowyText.emoji(
|
||||
view.icon.value,
|
||||
fontSize: emojiSize,
|
||||
lineHeight: textStyle?.height,
|
||||
optimizeEmojiAlign: true,
|
||||
)
|
||||
: FlowySvg(
|
||||
view.layout.icon,
|
||||
size: Size.square(iconSize + 2.0),
|
||||
),
|
||||
const HSpace(2),
|
||||
FlowyText(
|
||||
view.name,
|
||||
decoration: TextDecoration.underline,
|
||||
fontSize: textStyle?.fontSize,
|
||||
fontWeight: textStyle?.fontWeight,
|
||||
lineHeight: textStyle?.height,
|
||||
),
|
||||
const HSpace(4),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _NoAccessMentionPageBlock extends StatelessWidget {
|
||||
const _NoAccessMentionPageBlock({
|
||||
required this.textStyle,
|
||||
});
|
||||
|
||||
final TextStyle? textStyle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FlowyHover(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||
child: FlowyText(
|
||||
LocaleKeys.document_mention_noAccess.tr(),
|
||||
color: Theme.of(context).disabledColor,
|
||||
decoration: TextDecoration.underline,
|
||||
fontSize: textStyle?.fontSize,
|
||||
fontWeight: textStyle?.fontWeight,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MobileMentionPageBlock extends StatelessWidget {
|
||||
const _MobileMentionPageBlock({
|
||||
required this.view,
|
||||
required this.textStyle,
|
||||
required this.handleTap,
|
||||
required this.handleDoubleTap,
|
||||
});
|
||||
|
||||
final TextStyle? textStyle;
|
||||
final ViewPB view;
|
||||
final VoidCallback handleTap;
|
||||
final VoidCallback handleDoubleTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: handleTap,
|
||||
onDoubleTap: handleDoubleTap,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: _MentionPageBlockContent(
|
||||
view: view,
|
||||
textStyle: textStyle,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DesktopMentionPageBlock extends StatelessWidget {
|
||||
const _DesktopMentionPageBlock({
|
||||
required this.view,
|
||||
required this.textStyle,
|
||||
required this.handleTap,
|
||||
});
|
||||
|
||||
final TextStyle? textStyle;
|
||||
final ViewPB view;
|
||||
final VoidCallback handleTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: handleTap,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 2),
|
||||
child: FlowyHover(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: _MentionPageBlockContent(
|
||||
view: view,
|
||||
textStyle: textStyle,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/animated_gesture.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/aa_menu/_toolbar_theme.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/appflowy_mobile_toolbar.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
@ -106,9 +107,9 @@ class _AppFlowyMobileToolbarIconItemState
|
||||
final enable = widget.enable?.call() ?? true;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5),
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
child: AnimatedGestureDetector(
|
||||
scaleFactor: 0.95,
|
||||
onTapUp: () {
|
||||
widget.onTap();
|
||||
_rebuild();
|
||||
},
|
||||
|
@ -137,7 +137,6 @@ class EditorStyleCustomizer {
|
||||
textStyle: baseTextStyle.copyWith(
|
||||
fontSize: fontSize,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontStyle: FontStyle.italic,
|
||||
color: Colors.red,
|
||||
backgroundColor: Colors.grey.withOpacity(0.3),
|
||||
),
|
||||
|
@ -0,0 +1,168 @@
|
||||
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/mobile/presentation/base/animated_gesture.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart' show PlatformExtension;
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SyncErrorPage extends StatelessWidget {
|
||||
const SyncErrorPage({
|
||||
super.key,
|
||||
this.error,
|
||||
});
|
||||
|
||||
final FlowyError? error;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (PlatformExtension.isMobile) {
|
||||
return _MobileSyncErrorPage(error: error);
|
||||
} else {
|
||||
return _DesktopSyncErrorPage(error: error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _MobileSyncErrorPage extends StatelessWidget {
|
||||
const _MobileSyncErrorPage({
|
||||
this.error,
|
||||
});
|
||||
|
||||
final FlowyError? error;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedGestureDetector(
|
||||
scaleFactor: 0.99,
|
||||
onTapUp: () {
|
||||
getIt<ClipboardService>().setPlainText(error.toString());
|
||||
showToastNotification(
|
||||
context,
|
||||
message: LocaleKeys.message_copy_success.tr(),
|
||||
bottomPadding: 0,
|
||||
);
|
||||
},
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const FlowySvg(
|
||||
FlowySvgs.icon_warning_xl,
|
||||
blendMode: null,
|
||||
),
|
||||
const VSpace(16.0),
|
||||
FlowyText.medium(
|
||||
LocaleKeys.error_syncError.tr(),
|
||||
fontSize: 15,
|
||||
),
|
||||
const VSpace(8.0),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: FlowyText.regular(
|
||||
LocaleKeys.error_syncErrorHint.tr(),
|
||||
fontSize: 13,
|
||||
color: Theme.of(context).hintColor,
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 10,
|
||||
),
|
||||
),
|
||||
const VSpace(2.0),
|
||||
FlowyText.regular(
|
||||
'(${LocaleKeys.error_clickToCopy.tr()})',
|
||||
fontSize: 13,
|
||||
color: Theme.of(context).hintColor,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DesktopSyncErrorPage extends StatelessWidget {
|
||||
const _DesktopSyncErrorPage({
|
||||
this.error,
|
||||
});
|
||||
|
||||
final FlowyError? error;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedGestureDetector(
|
||||
scaleFactor: 0.995,
|
||||
onTapUp: () {
|
||||
getIt<ClipboardService>().setPlainText(error.toString());
|
||||
showToastNotification(
|
||||
context,
|
||||
message: LocaleKeys.message_copy_success.tr(),
|
||||
bottomPadding: 0,
|
||||
);
|
||||
},
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const FlowySvg(
|
||||
FlowySvgs.icon_warning_xl,
|
||||
blendMode: null,
|
||||
),
|
||||
const VSpace(16.0),
|
||||
FlowyText.medium(
|
||||
error?.code.toString() ?? '',
|
||||
fontSize: 16,
|
||||
),
|
||||
const VSpace(8.0),
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: LocaleKeys.errorDialog_howToFixFallbackHint1.tr(),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: 'Github',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
afLaunchUrlString(
|
||||
'https://github.com/AppFlowy-IO/AppFlowy/issues/new?template=bug_report.yaml',
|
||||
);
|
||||
},
|
||||
),
|
||||
TextSpan(
|
||||
text: LocaleKeys.errorDialog_howToFixFallbackHint2.tr(),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const VSpace(8.0),
|
||||
FlowyText.regular(
|
||||
'(${LocaleKeys.error_clickToCopy.tr()})',
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).hintColor,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.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:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
@ -51,6 +52,14 @@ class ExportTab extends StatelessWidget {
|
||||
svg: FlowySvgs.duplicate_s,
|
||||
onTap: () => _exportToClipboard(context),
|
||||
),
|
||||
if (kDebugMode) ...[
|
||||
const VSpace(10),
|
||||
_ExportButton(
|
||||
title: 'JSON (Debug Mode)',
|
||||
svg: FlowySvgs.duplicate_s,
|
||||
onTap: () => _exportJSON(context),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -64,6 +73,14 @@ class ExportTab extends StatelessWidget {
|
||||
svg: FlowySvgs.database_layout_m,
|
||||
onTap: () => _exportCSV(context),
|
||||
),
|
||||
if (kDebugMode) ...[
|
||||
const VSpace(10),
|
||||
_ExportButton(
|
||||
title: 'Raw Database Data (Debug Mode)',
|
||||
svg: FlowySvgs.duplicate_s,
|
||||
onTap: () => _exportRawDatabaseData(context),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -100,6 +117,22 @@ class ExportTab extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _exportJSON(BuildContext context) async {
|
||||
final viewName = context.read<ShareBloc>().state.viewName;
|
||||
final exportPath = await getIt<FilePickerService>().saveFile(
|
||||
dialogTitle: '',
|
||||
fileName: '${viewName.toFileName()}.json',
|
||||
);
|
||||
if (context.mounted && exportPath != null) {
|
||||
context.read<ShareBloc>().add(
|
||||
ShareEvent.share(
|
||||
ShareType.json,
|
||||
exportPath,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _exportCSV(BuildContext context) async {
|
||||
final viewName = context.read<ShareBloc>().state.viewName;
|
||||
final exportPath = await getIt<FilePickerService>().saveFile(
|
||||
@ -116,6 +149,22 @@ class ExportTab extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _exportRawDatabaseData(BuildContext context) async {
|
||||
final viewName = context.read<ShareBloc>().state.viewName;
|
||||
final exportPath = await getIt<FilePickerService>().saveFile(
|
||||
dialogTitle: '',
|
||||
fileName: '${viewName.toFileName()}.json',
|
||||
);
|
||||
if (context.mounted && exportPath != null) {
|
||||
context.read<ShareBloc>().add(
|
||||
ShareEvent.share(
|
||||
ShareType.rawDatabaseData,
|
||||
exportPath,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _exportToClipboard(BuildContext context) async {
|
||||
final documentExporter = DocumentExporter(context.read<ShareBloc>().view);
|
||||
final result = await documentExporter.export(DocumentExportType.markdown);
|
||||
|
@ -179,6 +179,14 @@ class ShareBloc extends Bloc<ShareEvent, ShareState> {
|
||||
(s) => FlowyResult.success(s.data),
|
||||
(f) => FlowyResult.failure(f),
|
||||
);
|
||||
} else if (type == ShareType.rawDatabaseData) {
|
||||
final exportResult = await BackendExportService.exportDatabaseAsRawData(
|
||||
view.id,
|
||||
);
|
||||
result = exportResult.fold(
|
||||
(s) => FlowyResult.success(s.data),
|
||||
(f) => FlowyResult.failure(f),
|
||||
);
|
||||
} else {
|
||||
result = await documentExporter.export(type.documentExportType);
|
||||
}
|
||||
@ -189,6 +197,8 @@ class ShareBloc extends Bloc<ShareEvent, ShareState> {
|
||||
case ShareType.markdown:
|
||||
case ShareType.html:
|
||||
case ShareType.csv:
|
||||
case ShareType.json:
|
||||
case ShareType.rawDatabaseData:
|
||||
File(path).writeAsStringSync(s);
|
||||
return FlowyResult.success(type);
|
||||
default:
|
||||
@ -208,9 +218,11 @@ enum ShareType {
|
||||
html,
|
||||
text,
|
||||
link,
|
||||
json,
|
||||
|
||||
// only available in database
|
||||
csv;
|
||||
csv,
|
||||
rawDatabaseData;
|
||||
|
||||
static List<ShareType> get unimplemented => [link];
|
||||
|
||||
@ -222,10 +234,16 @@ enum ShareType {
|
||||
return DocumentExportType.html;
|
||||
case ShareType.text:
|
||||
return DocumentExportType.text;
|
||||
case ShareType.json:
|
||||
return DocumentExportType.json;
|
||||
case ShareType.csv:
|
||||
throw UnsupportedError('DocumentShareType.csv is not supported');
|
||||
case ShareType.link:
|
||||
throw UnsupportedError('DocumentShareType.link is not supported');
|
||||
case ShareType.rawDatabaseData:
|
||||
throw UnsupportedError(
|
||||
'DocumentShareType.rawDatabaseData is not supported',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:appflowy/mobile/presentation/base/animated_gesture.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
@ -1524,9 +1525,10 @@ class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
|
||||
assert(debugCheckHasMaterialLocalizations(context));
|
||||
|
||||
if (widget.child != null) {
|
||||
return GestureDetector(
|
||||
onTap: widget.enabled ? showButtonMenu : null,
|
||||
child: widget.child,
|
||||
return AnimatedGestureDetector(
|
||||
scaleFactor: 0.99,
|
||||
onTapUp: widget.enabled ? showButtonMenu : null,
|
||||
child: widget.child!,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,8 @@ class FlowyRunner {
|
||||
[
|
||||
// this task should be first task, for handling platform errors.
|
||||
// don't catch errors in test mode
|
||||
if (!mode.isUnitTest) const PlatformErrorCatcherTask(),
|
||||
if (!mode.isUnitTest && !mode.isIntegrationTest)
|
||||
const PlatformErrorCatcherTask(),
|
||||
if (!mode.isUnitTest) const InitSentryTask(),
|
||||
// this task should be second task, for handling memory leak.
|
||||
// there's a flag named _enable in memory_leak_detector.dart. If it's false, the task will be ignored.
|
||||
|
@ -1,5 +1,7 @@
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../startup.dart';
|
||||
|
||||
@ -17,6 +19,23 @@ class PlatformErrorCatcherTask extends LaunchTask {
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
ErrorWidget.builder = (details) {
|
||||
if (kDebugMode) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: 30,
|
||||
color: Colors.red,
|
||||
child: FlowyText(
|
||||
'ERROR: ${details.exceptionAsString()}',
|
||||
color: Colors.white,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// hide the error widget in release mode
|
||||
return const SizedBox.shrink();
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -44,6 +44,7 @@ class MobileSignInScreen extends StatelessWidget {
|
||||
const Spacer(flex: 2),
|
||||
const Spacer(),
|
||||
Expanded(child: _buildSettingsButton(context)),
|
||||
if (Platform.isAndroid) const Spacer(),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/gesture.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/animated_gesture.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -12,4 +12,12 @@ class BackendExportService {
|
||||
final payload = DatabaseViewIdPB.create()..value = viewId;
|
||||
return DatabaseEventExportCSV(payload).send();
|
||||
}
|
||||
|
||||
static Future<FlowyResult<DatabaseExportDataPB, FlowyError>>
|
||||
exportDatabaseAsRawData(
|
||||
String viewId,
|
||||
) async {
|
||||
final payload = DatabaseViewIdPB.create()..value = viewId;
|
||||
return DatabaseEventExportRawDatabaseData(payload).send();
|
||||
}
|
||||
}
|
||||
|
@ -452,6 +452,9 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
|
||||
)..start(
|
||||
sectionChanged: (result) async {
|
||||
Log.info('did receive section views changed');
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
add(const SpaceEvent.didReceiveSpaceUpdate());
|
||||
},
|
||||
);
|
||||
|
@ -1,17 +1,17 @@
|
||||
import 'package:appflowy/shared/feature_flags.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/footer/sidebar_toast.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
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/shared/feature_flags.dart';
|
||||
import 'package:appflowy/startup/plugin/plugin.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/home_sizes.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/footer/sidebar_toast.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'sidebar_footer_button.dart';
|
||||
|
||||
class SidebarFooter extends StatelessWidget {
|
||||
const SidebarFooter({super.key});
|
||||
@ -26,52 +26,56 @@ class SidebarFooter extends StatelessWidget {
|
||||
return const SidebarToast();
|
||||
},
|
||||
),
|
||||
const Row(
|
||||
children: [
|
||||
Expanded(child: SidebarTrashButton()),
|
||||
// Enable it when the widget button is ready
|
||||
// SizedBox(
|
||||
// height: 16,
|
||||
// child: VerticalDivider(width: 1, color: Color(0x141F2329)),
|
||||
// ),
|
||||
// Expanded(child: SidebarWidgetButton()),
|
||||
],
|
||||
),
|
||||
const SidebarTemplateButton(),
|
||||
const SidebarTrashButton(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SidebarTemplateButton extends StatelessWidget {
|
||||
const SidebarTemplateButton({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SidebarFooterButton(
|
||||
leftIconSize: const Size.square(24.0),
|
||||
leftIcon: const Padding(
|
||||
padding: EdgeInsets.all(2.0),
|
||||
child: FlowySvg(
|
||||
FlowySvgs.icon_template_s,
|
||||
),
|
||||
),
|
||||
text: LocaleKeys.template_label.tr(),
|
||||
onTap: () => afLaunchUrlString('https://appflowy.io/templates'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SidebarTrashButton extends StatelessWidget {
|
||||
const SidebarTrashButton({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: HomeSizes.workspaceSectionHeight,
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: getIt<MenuSharedState>().notifier,
|
||||
builder: (context, value, child) {
|
||||
return FlowyButton(
|
||||
leftIcon: const FlowySvg(FlowySvgs.sidebar_footer_trash_m),
|
||||
leftIconSize: const Size.square(24.0),
|
||||
iconPadding: 8.0,
|
||||
margin: const EdgeInsets.all(4.0),
|
||||
text: FlowyText.regular(
|
||||
LocaleKeys.trash_text.tr(),
|
||||
lineHeight: 1.15,
|
||||
),
|
||||
onTap: () {
|
||||
getIt<MenuSharedState>().latestOpenView = null;
|
||||
getIt<TabsBloc>().add(
|
||||
TabsEvent.openPlugin(
|
||||
plugin: makePlugin(pluginType: PluginType.trash),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: getIt<MenuSharedState>().notifier,
|
||||
builder: (context, value, child) {
|
||||
return SidebarFooterButton(
|
||||
leftIconSize: const Size.square(24.0),
|
||||
leftIcon: const FlowySvg(
|
||||
FlowySvgs.sidebar_footer_trash_m,
|
||||
),
|
||||
text: LocaleKeys.trash_text.tr(),
|
||||
onTap: () {
|
||||
getIt<MenuSharedState>().latestOpenView = null;
|
||||
getIt<TabsBloc>().add(
|
||||
TabsEvent.openPlugin(
|
||||
plugin: makePlugin(pluginType: PluginType.trash),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,39 @@
|
||||
import 'package:appflowy/workspace/presentation/home/home_sizes.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// This button style is used in
|
||||
// - Trash button
|
||||
// - Template button
|
||||
class SidebarFooterButton extends StatelessWidget {
|
||||
const SidebarFooterButton({
|
||||
super.key,
|
||||
required this.leftIcon,
|
||||
required this.leftIconSize,
|
||||
required this.text,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
final Widget leftIcon;
|
||||
final Size leftIconSize;
|
||||
final String text;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: HomeSizes.workspaceSectionHeight,
|
||||
child: FlowyButton(
|
||||
leftIcon: leftIcon,
|
||||
leftIconSize: leftIconSize,
|
||||
iconPadding: 8.0,
|
||||
margin: const EdgeInsets.all(4.0),
|
||||
text: FlowyText.regular(
|
||||
text,
|
||||
lineHeight: 1.15,
|
||||
),
|
||||
onTap: onTap,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -48,6 +48,8 @@ enum ImportType {
|
||||
|
||||
bool get enableOnRelease {
|
||||
switch (this) {
|
||||
case ImportType.historyDatabase:
|
||||
case ImportType.historyDocument:
|
||||
case ImportType.databaseRawData:
|
||||
return kDebugMode;
|
||||
default:
|
||||
|
@ -97,6 +97,13 @@ class _NavigatorTextFieldDialogState extends State<NavigatorTextFieldDialog> {
|
||||
VSpace(Insets.xl),
|
||||
OkCancelButton(
|
||||
onOkPressed: () {
|
||||
if (newValue.isEmpty) {
|
||||
showToastNotification(
|
||||
context,
|
||||
message: LocaleKeys.space_spaceNameCannotBeEmpty.tr(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
widget.onConfirm(newValue, context);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
|
24
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
24
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -172,7 +172,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||
[[package]]
|
||||
name = "app-error"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -192,7 +192,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "appflowy-ai-client"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -837,7 +837,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"again",
|
||||
"anyhow",
|
||||
@ -888,7 +888,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"collab-entity",
|
||||
"collab-rt-entity",
|
||||
@ -901,7 +901,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-websocket"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
@ -1149,7 +1149,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-rt-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -1174,7 +1174,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-rt-protocol"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -1571,7 +1571,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
||||
[[package]]
|
||||
name = "database-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -3117,7 +3117,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
@ -3134,7 +3134,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -3566,7 +3566,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "infra"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -6169,7 +6169,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "shared-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
|
@ -53,7 +53,7 @@ collab-user = { version = "0.2" }
|
||||
# Run the script:
|
||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||
# ⚠️⚠️⚠️️
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "5badffc97b17984d5f25a178c0c5a477338039c4" }
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "cccdf76116f41631daa5533b56238aec323684de" }
|
||||
|
||||
[dependencies]
|
||||
serde_json.workspace = true
|
||||
|
24
frontend/appflowy_web_app/src-tauri/Cargo.lock
generated
24
frontend/appflowy_web_app/src-tauri/Cargo.lock
generated
@ -163,7 +163,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||
[[package]]
|
||||
name = "app-error"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -183,7 +183,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "appflowy-ai-client"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -811,7 +811,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"again",
|
||||
"anyhow",
|
||||
@ -862,7 +862,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"collab-entity",
|
||||
"collab-rt-entity",
|
||||
@ -875,7 +875,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-websocket"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
@ -1132,7 +1132,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-rt-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -1157,7 +1157,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-rt-protocol"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -1561,7 +1561,7 @@ checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
|
||||
[[package]]
|
||||
name = "database-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -3184,7 +3184,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
@ -3201,7 +3201,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -3638,7 +3638,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "infra"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -6233,7 +6233,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "shared-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
|
@ -52,7 +52,7 @@ collab-user = { version = "0.2" }
|
||||
# Run the script:
|
||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||
# ⚠️⚠️⚠️️
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "5badffc97b17984d5f25a178c0c5a477338039c4" }
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "cccdf76116f41631daa5533b56238aec323684de" }
|
||||
|
||||
[dependencies]
|
||||
serde_json.workspace = true
|
||||
|
@ -1,17 +1,15 @@
|
||||
import { FieldURLType } from '@/application/collab.type';
|
||||
import { notify } from '@/components/_shared/notify';
|
||||
import RightTopActionsToolbar from '@/components/editor/components/block-actions/RightTopActionsToolbar';
|
||||
import { EditorElementProps, FileNode } from '@/components/editor/editor.type';
|
||||
import { copyTextToClipboard } from '@/utils/copy';
|
||||
import { downloadFile } from '@/utils/download';
|
||||
import { renderDate } from '@/utils/time';
|
||||
import React, { forwardRef, memo, useCallback, useMemo, useState } from 'react';
|
||||
import { ReactComponent as FileIcon } from '@/assets/file_upload.svg';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const FileBlock = memo(
|
||||
forwardRef<HTMLDivElement, EditorElementProps<FileNode>>(({ node, children, ...attributes }, ref) => {
|
||||
const { url, name, url_type, uploaded_at } = useMemo(() => node.data || {}, [node.data]);
|
||||
const { url, name } = useMemo(() => node.data || {}, [node.data]);
|
||||
|
||||
const className = useMemo(() => {
|
||||
const classList = ['w-full bg-bg-body py-2'];
|
||||
@ -41,20 +39,6 @@ export const FileBlock = memo(
|
||||
}
|
||||
}, [url, name]);
|
||||
|
||||
const uploadTypePrefix = useMemo(() => {
|
||||
const time = renderDate(uploaded_at, 'MMM DD, YYYY', false);
|
||||
|
||||
if (url_type === FieldURLType.Upload) {
|
||||
return t('web.fileBlock.uploadedAt', {
|
||||
time,
|
||||
});
|
||||
} else {
|
||||
return t('web.fileBlock.linkedAt', {
|
||||
time,
|
||||
});
|
||||
}
|
||||
}, [uploaded_at, url_type, t]);
|
||||
|
||||
return (
|
||||
<div
|
||||
{...attributes}
|
||||
@ -75,9 +59,6 @@ export const FileBlock = memo(
|
||||
{url ?
|
||||
<>
|
||||
<div className={'w-full truncate'}>{name?.trim() || t('document.title.placeholder')}</div>
|
||||
<div className={'text-xs'}>
|
||||
{uploadTypePrefix}
|
||||
</div>
|
||||
</> :
|
||||
<div className={'text-text-caption'}>
|
||||
{t('web.fileBlock.empty')}
|
||||
|
5
frontend/resources/flowy_icons/16x/icon_template.svg
Normal file
5
frontend/resources/flowy_icons/16x/icon_template.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.6">
|
||||
<path d="M8.125 3.125H4.375C4.04348 3.125 3.72554 3.2567 3.49112 3.49112C3.2567 3.72554 3.125 4.04348 3.125 4.375V8.125C3.125 8.45652 3.2567 8.77446 3.49112 9.00888C3.72554 9.2433 4.04348 9.375 4.375 9.375H8.125C8.45652 9.375 8.77446 9.2433 9.00888 9.00888C9.2433 8.77446 9.375 8.45652 9.375 8.125V4.375C9.375 4.04348 9.2433 3.72554 9.00888 3.49112C8.77446 3.2567 8.45652 3.125 8.125 3.125ZM8.125 8.125H4.375V4.375H8.125V8.125ZM15.625 3.125H11.875C11.5435 3.125 11.2255 3.2567 10.9911 3.49112C10.7567 3.72554 10.625 4.04348 10.625 4.375V8.125C10.625 8.45652 10.7567 8.77446 10.9911 9.00888C11.2255 9.2433 11.5435 9.375 11.875 9.375H15.625C15.9565 9.375 16.2745 9.2433 16.5089 9.00888C16.7433 8.77446 16.875 8.45652 16.875 8.125V4.375C16.875 4.04348 16.7433 3.72554 16.5089 3.49112C16.2745 3.2567 15.9565 3.125 15.625 3.125ZM15.625 8.125H11.875V4.375H15.625V8.125ZM8.125 10.625H4.375C4.04348 10.625 3.72554 10.7567 3.49112 10.9911C3.2567 11.2255 3.125 11.5435 3.125 11.875V15.625C3.125 15.9565 3.2567 16.2745 3.49112 16.5089C3.72554 16.7433 4.04348 16.875 4.375 16.875H8.125C8.45652 16.875 8.77446 16.7433 9.00888 16.5089C9.2433 16.2745 9.375 15.9565 9.375 15.625V11.875C9.375 11.5435 9.2433 11.2255 9.00888 10.9911C8.77446 10.7567 8.45652 10.625 8.125 10.625ZM8.125 15.625H4.375V11.875H8.125V15.625ZM15.625 10.625H11.875C11.5435 10.625 11.2255 10.7567 10.9911 10.9911C10.7567 11.2255 10.625 11.5435 10.625 11.875V15.625C10.625 15.9565 10.7567 16.2745 10.9911 16.5089C11.2255 16.7433 11.5435 16.875 11.875 16.875H15.625C15.9565 16.875 16.2745 16.7433 16.5089 16.5089C16.7433 16.2745 16.875 15.9565 16.875 15.625V11.875C16.875 11.5435 16.7433 11.2255 16.5089 10.9911C16.2745 10.7567 15.9565 10.625 15.625 10.625ZM15.625 15.625H11.875V11.875H15.625V15.625Z" fill="#101012"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
5
frontend/resources/flowy_icons/40x/icon_warning.svg
Normal file
5
frontend/resources/flowy_icons/40x/icon_warning.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<ellipse cx="21.9997" cy="21.6966" rx="20.1667" ry="19.8885" fill="#FF811A"/>
|
||||
<path d="M22.0003 12.6562C20.9878 12.6562 20.167 13.4657 20.167 14.4643V23.5045C20.167 24.5031 20.9878 25.3126 22.0003 25.3126C23.0128 25.3126 23.8337 24.5031 23.8337 23.5045V14.4643C23.8337 13.4657 23.0128 12.6562 22.0003 12.6562Z" fill="white"/>
|
||||
<path d="M22.0003 30.7367C23.0128 30.7367 23.8337 29.9272 23.8337 28.9287C23.8337 27.9301 23.0128 27.1206 22.0003 27.1206C20.9878 27.1206 20.167 27.9301 20.167 28.9287C20.167 29.9272 20.9878 30.7367 22.0003 30.7367Z" fill="white"/>
|
||||
</svg>
|
After Width: | Height: | Size: 661 B |
@ -1897,6 +1897,8 @@
|
||||
"errorDialog": {
|
||||
"title": "@:appName Error",
|
||||
"howToFixFallback": "We're sorry for the inconvenience! Submit an issue on our GitHub page that describes your error.",
|
||||
"howToFixFallbackHint1": "We're sorry for the inconvenience! Submit an issue on our ",
|
||||
"howToFixFallbackHint2": " page that describes your error.",
|
||||
"github": "View on GitHub"
|
||||
},
|
||||
"search": {
|
||||
@ -2051,7 +2053,10 @@
|
||||
},
|
||||
"error": {
|
||||
"weAreSorry": "We're sorry",
|
||||
"loadingViewError": "We're having trouble loading this view. Please check your internet connection, refresh the app, and do not hesitate to reach out to the team if the issue continues."
|
||||
"loadingViewError": "We're having trouble loading this view. Please check your internet connection, refresh the app, and do not hesitate to reach out to the team if the issue continues.",
|
||||
"syncError": "Data is not synced from another device",
|
||||
"syncErrorHint": "Please reopen this page on the device where it was last edited, then open it again on the current device.",
|
||||
"clickToCopy": "Click to copy error code"
|
||||
},
|
||||
"editor": {
|
||||
"bold": "Bold",
|
||||
@ -2317,7 +2322,8 @@
|
||||
"quicklySwitch": "Quickly switch to the next space",
|
||||
"duplicate": "Duplicate Space",
|
||||
"movePageToSpace": "Move page to space",
|
||||
"switchSpace": "Switch space"
|
||||
"switchSpace": "Switch space",
|
||||
"spaceNameCannotBeEmpty": "Space name cannot be empty"
|
||||
},
|
||||
"publish": {
|
||||
"hasNotBeenPublished": "This page hasn't been published yet",
|
||||
@ -2484,7 +2490,8 @@
|
||||
"addRelatedTemplate": "Add related template",
|
||||
"removeRelatedTemplate": "Remove related template",
|
||||
"uploadAvatar": "Upload avatar",
|
||||
"searchInCategory": "Search in {category}"
|
||||
"searchInCategory": "Search in {category}",
|
||||
"label": "Template"
|
||||
},
|
||||
"fileDropzone": {
|
||||
"dropFile": "Click or drag file to this area to upload",
|
||||
|
24
frontend/rust-lib/Cargo.lock
generated
24
frontend/rust-lib/Cargo.lock
generated
@ -163,7 +163,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||
[[package]]
|
||||
name = "app-error"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -183,7 +183,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "appflowy-ai-client"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -729,7 +729,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"again",
|
||||
"anyhow",
|
||||
@ -780,7 +780,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"collab-entity",
|
||||
"collab-rt-entity",
|
||||
@ -793,7 +793,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-websocket"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
@ -1010,7 +1010,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-rt-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -1035,7 +1035,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-rt-protocol"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -1395,7 +1395,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
||||
[[package]]
|
||||
name = "database-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -2795,7 +2795,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
@ -2812,7 +2812,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -3177,7 +3177,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "infra"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -5377,7 +5377,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "shared-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
|
@ -100,8 +100,8 @@ dashmap = "6.0.1"
|
||||
# Run the script.add_workspace_members:
|
||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||
# ⚠️⚠️⚠️️
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "5badffc97b17984d5f25a178c0c5a477338039c4" }
|
||||
client-api-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "5badffc97b17984d5f25a178c0c5a477338039c4" }
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "cccdf76116f41631daa5533b56238aec323684de" }
|
||||
client-api-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "cccdf76116f41631daa5533b56238aec323684de" }
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 0
|
||||
|
@ -377,7 +377,7 @@ impl FolderOperationHandler for DatabaseFolderOperation {
|
||||
}
|
||||
|
||||
async fn duplicate_view(&self, view_id: &str) -> Result<Bytes, FlowyError> {
|
||||
let delta_bytes = self.0.duplicate_database(view_id).await?;
|
||||
let delta_bytes = self.0.get_database_json_bytes(view_id).await?;
|
||||
Ok(Bytes::from(delta_bytes))
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@ impl UserStatusCallback for UserStatusCallbackImpl {
|
||||
cloud_config: &Option<UserCloudConfig>,
|
||||
user_workspace: &UserWorkspace,
|
||||
_device_id: &str,
|
||||
authenticator: &Authenticator,
|
||||
) -> FlowyResult<()> {
|
||||
self
|
||||
.server_provider
|
||||
@ -64,7 +65,10 @@ impl UserStatusCallback for UserStatusCallbackImpl {
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
self.database_manager.initialize(user_id).await?;
|
||||
self
|
||||
.database_manager
|
||||
.initialize(user_id, authenticator == &Authenticator::Local)
|
||||
.await?;
|
||||
self.document_manager.initialize(user_id).await?;
|
||||
Ok(())
|
||||
}
|
||||
@ -74,6 +78,7 @@ impl UserStatusCallback for UserStatusCallbackImpl {
|
||||
user_id: i64,
|
||||
user_workspace: &UserWorkspace,
|
||||
device_id: &str,
|
||||
authenticator: &Authenticator,
|
||||
) -> FlowyResult<()> {
|
||||
event!(
|
||||
tracing::Level::TRACE,
|
||||
@ -86,7 +91,10 @@ impl UserStatusCallback for UserStatusCallbackImpl {
|
||||
.folder_manager
|
||||
.initialize_with_workspace_id(user_id)
|
||||
.await?;
|
||||
self.database_manager.initialize(user_id).await?;
|
||||
self
|
||||
.database_manager
|
||||
.initialize(user_id, authenticator.is_local())
|
||||
.await?;
|
||||
self.document_manager.initialize(user_id).await?;
|
||||
Ok(())
|
||||
}
|
||||
@ -97,6 +105,7 @@ impl UserStatusCallback for UserStatusCallbackImpl {
|
||||
user_profile: &UserProfile,
|
||||
user_workspace: &UserWorkspace,
|
||||
device_id: &str,
|
||||
authenticator: &Authenticator,
|
||||
) -> FlowyResult<()> {
|
||||
self
|
||||
.server_provider
|
||||
@ -156,7 +165,7 @@ impl UserStatusCallback for UserStatusCallbackImpl {
|
||||
|
||||
self
|
||||
.database_manager
|
||||
.initialize_with_new_user(user_profile.uid)
|
||||
.initialize_with_new_user(user_profile.uid, authenticator.is_local())
|
||||
.await
|
||||
.context("DatabaseManager error")?;
|
||||
|
||||
@ -173,12 +182,20 @@ impl UserStatusCallback for UserStatusCallbackImpl {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn open_workspace(&self, user_id: i64, user_workspace: &UserWorkspace) -> FlowyResult<()> {
|
||||
async fn open_workspace(
|
||||
&self,
|
||||
user_id: i64,
|
||||
user_workspace: &UserWorkspace,
|
||||
authenticator: &Authenticator,
|
||||
) -> FlowyResult<()> {
|
||||
self
|
||||
.folder_manager
|
||||
.initialize_with_workspace_id(user_id)
|
||||
.await?;
|
||||
self.database_manager.initialize(user_id).await?;
|
||||
self
|
||||
.database_manager
|
||||
.initialize(user_id, authenticator.is_local())
|
||||
.await?;
|
||||
self.document_manager.initialize(user_id).await?;
|
||||
self.ai_manager.initialize(&user_workspace.id).await?;
|
||||
self.storage_manager.initialize(&user_workspace.id).await;
|
||||
|
@ -4,6 +4,9 @@ use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
pub enum DatabaseExportDataType {
|
||||
#[default]
|
||||
CSV = 0,
|
||||
|
||||
// DatabaseData
|
||||
RawDatabaseData = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug, ProtoBuf, Default, Clone)]
|
||||
|
@ -1027,6 +1027,20 @@ pub(crate) async fn export_csv_handler(
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||
pub(crate) async fn export_raw_database_data_handler(
|
||||
data: AFPluginData<DatabaseViewIdPB>,
|
||||
manager: AFPluginState<Weak<DatabaseManager>>,
|
||||
) -> DataResult<DatabaseExportDataPB, FlowyError> {
|
||||
let manager = upgrade_manager(manager)?;
|
||||
let view_id = data.into_inner().value;
|
||||
let data = manager.get_database_json_string(&view_id).await?;
|
||||
data_result_ok(DatabaseExportDataPB {
|
||||
export_type: DatabaseExportDataType::RawDatabaseData,
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||
pub(crate) async fn get_snapshots_handler(
|
||||
data: AFPluginData<DatabaseViewIdPB>,
|
||||
|
@ -77,6 +77,7 @@ pub fn init(database_manager: Weak<DatabaseManager>) -> AFPlugin {
|
||||
.event(DatabaseEvent::CreateDatabaseView, create_database_view)
|
||||
// Export
|
||||
.event(DatabaseEvent::ExportCSV, export_csv_handler)
|
||||
.event(DatabaseEvent::ExportRawDatabaseData, export_raw_database_data_handler)
|
||||
.event(DatabaseEvent::GetDatabaseSnapshots, get_snapshots_handler)
|
||||
// Field settings
|
||||
.event(DatabaseEvent::GetFieldSettings, get_field_settings_handler)
|
||||
@ -385,4 +386,7 @@ pub enum DatabaseEvent {
|
||||
|
||||
#[event(input = "DatabaseViewIdPB", output = "RepeatedRowMetaPB")]
|
||||
GetAllRows = 177,
|
||||
|
||||
#[event(input = "DatabaseViewIdPB", output = "DatabaseExportDataPB")]
|
||||
ExportRawDatabaseData = 178,
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ impl DatabaseManager {
|
||||
}
|
||||
|
||||
/// When initialize with new workspace, all the resources will be cleared.
|
||||
pub async fn initialize(&self, uid: i64) -> FlowyResult<()> {
|
||||
pub async fn initialize(&self, uid: i64, is_local_user: bool) -> FlowyResult<()> {
|
||||
// 1. Clear all existing tasks
|
||||
self.task_scheduler.write().await.clear_task();
|
||||
// 2. Release all existing editors
|
||||
@ -99,6 +99,7 @@ impl DatabaseManager {
|
||||
|
||||
let collab_db = self.user.collab_db(uid)?;
|
||||
let collab_service = WorkspaceDatabaseCollabServiceImpl::new(
|
||||
is_local_user,
|
||||
self.user.clone(),
|
||||
self.collab_builder.clone(),
|
||||
self.cloud_service.clone(),
|
||||
@ -168,8 +169,12 @@ impl DatabaseManager {
|
||||
skip_all,
|
||||
err
|
||||
)]
|
||||
pub async fn initialize_with_new_user(&self, user_id: i64) -> FlowyResult<()> {
|
||||
self.initialize(user_id).await?;
|
||||
pub async fn initialize_with_new_user(
|
||||
&self,
|
||||
user_id: i64,
|
||||
is_local_user: bool,
|
||||
) -> FlowyResult<()> {
|
||||
self.initialize(user_id, is_local_user).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -340,7 +345,7 @@ impl DatabaseManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn duplicate_database(&self, view_id: &str) -> FlowyResult<Vec<u8>> {
|
||||
pub async fn get_database_json_bytes(&self, view_id: &str) -> FlowyResult<Vec<u8>> {
|
||||
let lock = self.workspace_database()?;
|
||||
let wdb = lock.read().await;
|
||||
let data = wdb.get_database_data(view_id).await?;
|
||||
@ -348,6 +353,14 @@ impl DatabaseManager {
|
||||
Ok(json_bytes)
|
||||
}
|
||||
|
||||
pub async fn get_database_json_string(&self, view_id: &str) -> FlowyResult<String> {
|
||||
let lock = self.workspace_database()?;
|
||||
let wdb = lock.read().await;
|
||||
let data = wdb.get_database_data(view_id).await?;
|
||||
let json_string = serde_json::to_string(&data)?;
|
||||
Ok(json_string)
|
||||
}
|
||||
|
||||
/// Create a new database with the given data that can be deserialized to [DatabaseData].
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
pub async fn create_database_with_database_data(
|
||||
@ -630,6 +643,7 @@ impl DatabaseManager {
|
||||
}
|
||||
|
||||
struct WorkspaceDatabaseCollabServiceImpl {
|
||||
is_local_user: bool,
|
||||
user: Arc<dyn DatabaseUser>,
|
||||
collab_builder: Arc<AppFlowyCollabBuilder>,
|
||||
persistence: Arc<dyn DatabaseCollabPersistenceService>,
|
||||
@ -639,12 +653,14 @@ struct WorkspaceDatabaseCollabServiceImpl {
|
||||
|
||||
impl WorkspaceDatabaseCollabServiceImpl {
|
||||
fn new(
|
||||
is_local_user: bool,
|
||||
user: Arc<dyn DatabaseUser>,
|
||||
collab_builder: Arc<AppFlowyCollabBuilder>,
|
||||
cloud_service: Arc<dyn DatabaseCloudService>,
|
||||
) -> Self {
|
||||
let persistence = DatabasePersistenceImpl { user: user.clone() };
|
||||
Self {
|
||||
is_local_user,
|
||||
user,
|
||||
collab_builder,
|
||||
persistence: Arc::new(persistence),
|
||||
@ -783,15 +799,18 @@ impl DatabaseCollabService for WorkspaceDatabaseCollabServiceImpl {
|
||||
DataSource::from(encode_collab)
|
||||
},
|
||||
Ok(None) => {
|
||||
info!(
|
||||
"build collab: {}:{} with empty encode collab",
|
||||
collab_type, object_id
|
||||
);
|
||||
// when collab not exist, create a default collab
|
||||
CollabPersistenceImpl {
|
||||
persistence: Some(self.persistence.clone()),
|
||||
if self.is_local_user {
|
||||
CollabPersistenceImpl {
|
||||
persistence: Some(self.persistence.clone()),
|
||||
}
|
||||
.into()
|
||||
} else {
|
||||
error!(
|
||||
"build collab: {}:{} with empty encode collab",
|
||||
collab_type, object_id
|
||||
);
|
||||
return Err(DatabaseError::RecordNotFound);
|
||||
}
|
||||
.into()
|
||||
},
|
||||
Err(err) => {
|
||||
error!("build collab: failed to get encode collab: {}", err);
|
||||
|
@ -5,7 +5,7 @@ use crate::services::cell::{apply_cell_changeset, get_cell_protobuf, CellCache};
|
||||
use crate::services::database::database_observe::*;
|
||||
use crate::services::database::util::database_view_setting_pb_from_view;
|
||||
use crate::services::database_view::{
|
||||
DatabaseViewChanged, DatabaseViewOperation, DatabaseViews, EditorByViewId,
|
||||
DatabaseViewChanged, DatabaseViewEditor, DatabaseViewOperation, DatabaseViews, EditorByViewId,
|
||||
};
|
||||
use crate::services::field::type_option_transform::transform_type_option;
|
||||
use crate::services::field::{
|
||||
@ -1355,57 +1355,7 @@ impl DatabaseEditor {
|
||||
}
|
||||
|
||||
let row_orders = self.database.read().await.get_row_orders_for_view(view_id);
|
||||
let cloned_database = Arc::downgrade(&self.database);
|
||||
let cloned_row_orders = row_orders.clone();
|
||||
let opening_database_views = self.database_views.clone();
|
||||
tokio::spawn(async move {
|
||||
const CHUNK_SIZE: usize = 10;
|
||||
let apply_filter_and_sort =
|
||||
|mut loaded_rows, opening_database_views: Arc<DatabaseViews>| async move {
|
||||
for database_view in opening_database_views.editors().await {
|
||||
if database_view.has_filters().await {
|
||||
database_view
|
||||
.v_filter_rows_and_notify(&mut loaded_rows)
|
||||
.await;
|
||||
}
|
||||
|
||||
if database_view.has_sorts().await {
|
||||
database_view.v_sort_rows_and_notify(&mut loaded_rows).await;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut loaded_rows = vec![];
|
||||
for chunk_row_orders in cloned_row_orders.chunks(CHUNK_SIZE) {
|
||||
match cloned_database.upgrade() {
|
||||
None => break,
|
||||
Some(database) => {
|
||||
for row_order in chunk_row_orders {
|
||||
if let Some(database_row) =
|
||||
database.read().await.init_database_row(&row_order.id).await
|
||||
{
|
||||
if let Some(row) = database_row.read().await.get_row() {
|
||||
loaded_rows.push(Arc::new(row));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stop init database rows
|
||||
if new_token.is_cancelled() {
|
||||
return;
|
||||
}
|
||||
|
||||
if loaded_rows.len() % 1000 == 0 {
|
||||
apply_filter_and_sort(loaded_rows.clone(), opening_database_views.clone()).await;
|
||||
}
|
||||
},
|
||||
}
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
|
||||
apply_filter_and_sort(loaded_rows.clone(), opening_database_views).await;
|
||||
});
|
||||
|
||||
// Collect database details in a single block holding the `read` lock
|
||||
let (database_id, fields, is_linked) = {
|
||||
let database = self.database.read().await;
|
||||
@ -1431,6 +1381,54 @@ impl DatabaseEditor {
|
||||
fields.len(),
|
||||
rows.len()
|
||||
);
|
||||
|
||||
trace!("[Database]: start loading rows");
|
||||
let cloned_database = Arc::downgrade(&self.database);
|
||||
let view_editor = self.database_views.get_view_editor(view_id).await?;
|
||||
tokio::spawn(async move {
|
||||
const CHUNK_SIZE: usize = 10;
|
||||
let apply_filter_and_sort =
|
||||
|mut loaded_rows: Vec<Arc<Row>>, view_editor: Arc<DatabaseViewEditor>| async move {
|
||||
if view_editor.has_filters().await {
|
||||
trace!("[Database]: filtering rows:{}", loaded_rows.len());
|
||||
view_editor.v_filter_rows_and_notify(&mut loaded_rows).await;
|
||||
}
|
||||
|
||||
if view_editor.has_sorts().await {
|
||||
trace!("[Database]: sorting rows:{}", loaded_rows.len());
|
||||
view_editor.v_sort_rows_and_notify(&mut loaded_rows).await;
|
||||
}
|
||||
};
|
||||
|
||||
let mut loaded_rows = vec![];
|
||||
for chunk_row_orders in cloned_row_orders.chunks(CHUNK_SIZE) {
|
||||
match cloned_database.upgrade() {
|
||||
None => break,
|
||||
Some(database) => {
|
||||
for row_order in chunk_row_orders {
|
||||
if let Some(database_row) =
|
||||
database.read().await.init_database_row(&row_order.id).await
|
||||
{
|
||||
if let Some(row) = database_row.read().await.get_row() {
|
||||
loaded_rows.push(Arc::new(row));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stop init database rows
|
||||
if new_token.is_cancelled() {
|
||||
info!("[Database]: stop loading database rows");
|
||||
return;
|
||||
}
|
||||
},
|
||||
}
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
|
||||
info!("[Database]: Finish loading rows: {}", loaded_rows.len());
|
||||
apply_filter_and_sort(loaded_rows.clone(), view_editor).await;
|
||||
});
|
||||
|
||||
Ok::<_, FlowyError>(DatabasePB {
|
||||
id: database_id,
|
||||
fields,
|
||||
|
@ -11,7 +11,7 @@ use flowy_error::FlowyResult;
|
||||
use lib_infra::priority_task::{QualityOfService, Task, TaskContent, TaskDispatcher};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::error;
|
||||
use tracing::{error, trace};
|
||||
|
||||
use crate::entities::filter_entities::*;
|
||||
use crate::entities::{FieldType, InsertedRowPB, RowMetaPB};
|
||||
@ -74,14 +74,13 @@ impl FilterController {
|
||||
let mut need_save = false;
|
||||
|
||||
let mut filters = delegate.get_all_filters(view_id).await;
|
||||
trace!("[Database]: filters: {:?}", filters);
|
||||
let mut filtering_field_ids: HashMap<String, Vec<String>> = HashMap::new();
|
||||
|
||||
for filter in filters.iter() {
|
||||
filter.get_all_filtering_field_ids(&mut filtering_field_ids);
|
||||
}
|
||||
|
||||
let mut delete_filter_ids = vec![];
|
||||
|
||||
for (field_id, filter_ids) in &filtering_field_ids {
|
||||
if !field_ids.contains(field_id) {
|
||||
need_save = true;
|
||||
@ -385,7 +384,6 @@ impl FilterController {
|
||||
invisible_rows,
|
||||
visible_rows,
|
||||
};
|
||||
tracing::trace!("filter result {:?}", notification);
|
||||
let _ = self
|
||||
.notifier
|
||||
.send(DatabaseViewChanged::FilterNotification(notification));
|
||||
|
@ -290,6 +290,7 @@ pub trait UserStatusCallback: Send + Sync + 'static {
|
||||
_cloud_config: &Option<UserCloudConfig>,
|
||||
_user_workspace: &UserWorkspace,
|
||||
_device_id: &str,
|
||||
_authenticator: &Authenticator,
|
||||
) -> FlowyResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
@ -299,6 +300,7 @@ pub trait UserStatusCallback: Send + Sync + 'static {
|
||||
_user_id: i64,
|
||||
_user_workspace: &UserWorkspace,
|
||||
_device_id: &str,
|
||||
_authenticator: &Authenticator,
|
||||
) -> FlowyResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
@ -309,6 +311,7 @@ pub trait UserStatusCallback: Send + Sync + 'static {
|
||||
_user_profile: &UserProfile,
|
||||
_user_workspace: &UserWorkspace,
|
||||
_device_id: &str,
|
||||
_authenticator: &Authenticator,
|
||||
) -> FlowyResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
@ -320,6 +323,7 @@ pub trait UserStatusCallback: Send + Sync + 'static {
|
||||
&self,
|
||||
_user_id: i64,
|
||||
_user_workspace: &UserWorkspace,
|
||||
_authenticator: &Authenticator,
|
||||
) -> FlowyResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
@ -280,6 +280,7 @@ impl UserManager {
|
||||
&cloud_config,
|
||||
&session.user_workspace,
|
||||
&self.authenticate_user.user_config.device_id,
|
||||
&user.authenticator,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
@ -352,6 +353,7 @@ impl UserManager {
|
||||
user_profile.uid,
|
||||
&latest_workspace,
|
||||
&self.authenticate_user.user_config.device_id,
|
||||
&authenticator,
|
||||
)
|
||||
.await?;
|
||||
send_auth_state_notification(AuthStateChangedPB {
|
||||
@ -443,6 +445,7 @@ impl UserManager {
|
||||
new_user_profile,
|
||||
&new_session.user_workspace,
|
||||
&self.authenticate_user.user_config.device_id,
|
||||
authenticator,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
@ -196,7 +196,7 @@ impl UserManager {
|
||||
.user_status_callback
|
||||
.read()
|
||||
.await
|
||||
.open_workspace(uid, &user_workspace)
|
||||
.open_workspace(uid, &user_workspace, &user_profile.authenticator)
|
||||
.await
|
||||
{
|
||||
error!("Open workspace failed: {:?}", err);
|
||||
|
Loading…
Reference in New Issue
Block a user