Merge branch 'AppFlowy-IO:main' into main
13
CHANGELOG.md
@ -1,4 +1,17 @@
|
||||
# Release Notes
|
||||
## Version 0.6.8 - 22/08/2024
|
||||
### New Features
|
||||
- Optimized date picker and mention block.
|
||||
- Added the ability to open database row on mobile.
|
||||
- Added the ability to invite members to workspace on mobile.
|
||||
- Added support for Monochrome theme on Android.
|
||||
- Added AI Bubble button on homepage on mobile.
|
||||
- Settings, trash, members and help & support have been moved into the settings pop up menu.
|
||||
|
||||
### Bug Fixes
|
||||
- Removed Wayland header from AppImage build
|
||||
- Fixed the issue where pasting web image on mobile failed.
|
||||
|
||||
## Version 0.6.7 - 13/08/2024
|
||||
### New Features
|
||||
- Redesigned the icon picker design on Desktop.
|
||||
|
BIN
doc/readme/desktop_guide_1.jpg
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
doc/readme/desktop_guide_2.jpg
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
doc/readme/getting_started_1.png
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
doc/readme/mobile_guide_1.png
Normal file
After Width: | Height: | Size: 115 KiB |
BIN
doc/readme/mobile_guide_2.png
Normal file
After Width: | Height: | Size: 138 KiB |
BIN
doc/readme/mobile_guide_3.png
Normal file
After Width: | Height: | Size: 108 KiB |
BIN
doc/readme/mobile_guide_4.png
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
doc/readme/mobile_guide_5.png
Normal file
After Width: | Height: | Size: 77 KiB |
@ -26,7 +26,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
|
||||
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
|
||||
CARGO_MAKE_CRATE_NAME = "dart-ffi"
|
||||
LIB_NAME = "dart_ffi"
|
||||
APPFLOWY_VERSION = "0.6.7"
|
||||
APPFLOWY_VERSION = "0.6.8"
|
||||
FLUTTER_DESKTOP_FEATURES = "dart"
|
||||
PRODUCT_NAME = "AppFlowy"
|
||||
MACOSX_DEPLOYMENT_TARGET = "11.0"
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_page_block.dart';
|
||||
import 'package:appflowy/shared/flowy_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(AppFlowyErrorPage), findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import 'util.dart';
|
||||
extension AppFlowyAuthTest on WidgetTester {
|
||||
Future<void> tapGoogleLoginInButton() async {
|
||||
await tapButton(
|
||||
find.byKey(const Key('signInWithGoogleButton')),
|
||||
find.byKey(signInWithGoogleButtonKey),
|
||||
);
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ extension AppFlowyAuthTest on WidgetTester {
|
||||
}
|
||||
|
||||
void expectToSeeGoogleLoginButton() {
|
||||
expect(find.byKey(const Key('signInWithGoogleButton')), findsOneWidget);
|
||||
expect(find.byKey(signInWithGoogleButtonKey), findsOneWidget);
|
||||
}
|
||||
|
||||
void assertSwitchValue(Finder finder, bool value) {
|
||||
|
@ -372,6 +372,7 @@
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = AppFlowy;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@ -383,6 +384,8 @@
|
||||
STRIP_STYLE = "non-global";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
@ -511,6 +514,7 @@
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = AppFlowy;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@ -522,6 +526,8 @@
|
||||
STRIP_STYLE = "non-global";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
@ -545,6 +551,7 @@
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = AppFlowy;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@ -556,6 +563,8 @@
|
||||
STRIP_STYLE = "non-global";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
|
@ -1,75 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>AppFlowy requires access to the camera.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>AppFlowy requires access to the photo library.</string>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true />
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleLocalizations</key>
|
||||
<array>
|
||||
<string>en</string>
|
||||
</array>
|
||||
<key>FLTEnableImpeller</key>
|
||||
<false />
|
||||
<key>CFBundleName</key>
|
||||
<string>AppFlowy</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string></string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>appflowy-flutter</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true />
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true />
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false />
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleLocalizations</key>
|
||||
<array>
|
||||
<string>en</string>
|
||||
</array>
|
||||
<key>CFBundleName</key>
|
||||
<string>AppFlowy</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>CFBundleURLName</key>
|
||||
<string></string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>appflowy-flutter</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>FLTEnableImpeller</key>
|
||||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>AppFlowy requires access to the camera.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>AppFlowy requires access to the photo library.</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -4,5 +4,9 @@
|
||||
<dict>
|
||||
<key>aps-environment</key>
|
||||
<string>development</string>
|
||||
<key>com.apple.developer.applesignin</key>
|
||||
<array>
|
||||
<string>Default</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -65,7 +65,7 @@ class _MobileBottomSheetRenameWidgetState
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0,
|
||||
),
|
||||
textColor: Colors.white,
|
||||
fontColor: Colors.white,
|
||||
fillColor: Theme.of(context).primaryColor,
|
||||
onPressed: () {
|
||||
widget.onRename(controller.text);
|
||||
|
@ -64,6 +64,10 @@ class _MobileViewItemBottomSheetState extends State<MobileViewItemBottomSheet> {
|
||||
case MobileViewItemBottomSheetBodyAction.duplicate:
|
||||
Navigator.pop(context);
|
||||
context.read<ViewBloc>().add(const ViewEvent.duplicate());
|
||||
showToastNotification(
|
||||
context,
|
||||
message: LocaleKeys.button_duplicateSuccessfully.tr(),
|
||||
);
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.share:
|
||||
// unimplemented
|
||||
@ -79,6 +83,12 @@ class _MobileViewItemBottomSheetState extends State<MobileViewItemBottomSheet> {
|
||||
context
|
||||
.read<FavoriteBloc>()
|
||||
.add(FavoriteEvent.toggle(widget.view));
|
||||
showToastNotification(
|
||||
context,
|
||||
message: !widget.view.isFavorite
|
||||
? LocaleKeys.button_favoriteSuccessfully.tr()
|
||||
: LocaleKeys.button_unfavoriteSuccessfully.tr(),
|
||||
);
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.removeFromRecent:
|
||||
_removeFromRecent(context);
|
||||
|
@ -3,13 +3,13 @@ import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/database/board/board.dart';
|
||||
import 'package:appflowy/mobile/presentation/database/board/widgets/group_card_header.dart';
|
||||
import 'package:appflowy/mobile/presentation/database/card/card.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
||||
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
||||
import 'package:appflowy/plugins/database/board/application/board_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/field_type_extension.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/card/card.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/card_cell_builder.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/card_cell_style_maps/mobile_board_card_cell_style.dart';
|
||||
import 'package:appflowy/shared/flowy_error_page.dart';
|
||||
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
@ -69,10 +69,8 @@ class _MobileBoardPageState extends State<MobileBoardPage> {
|
||||
loading: (_) => const Center(
|
||||
child: CircularProgressIndicator.adaptive(),
|
||||
),
|
||||
error: (err) => FlowyMobileStateContainer.error(
|
||||
emoji: '🛸',
|
||||
title: LocaleKeys.board_mobile_failedToLoad.tr(),
|
||||
errorMsg: err.toString(),
|
||||
error: (err) => AppFlowyErrorPage(
|
||||
error: err.error,
|
||||
),
|
||||
ready: (data) => const _BoardContent(),
|
||||
orElse: () => const SizedBox.shrink(),
|
||||
|
@ -45,6 +45,7 @@ class MobileFolders extends StatelessWidget {
|
||||
SpaceEvent.reset(
|
||||
user,
|
||||
state.currentWorkspace?.workspaceId ?? workspaceId,
|
||||
false,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -177,7 +177,13 @@ class _HomePageState extends State<_HomePage> {
|
||||
getIt<CachedRecentService>().reset();
|
||||
mCurrentWorkspace.value = state.currentWorkspace;
|
||||
|
||||
_showResultDialog(context, state);
|
||||
Debounce.debounce(
|
||||
'workspace_action_result',
|
||||
const Duration(milliseconds: 150),
|
||||
() {
|
||||
_showResultDialog(context, state);
|
||||
},
|
||||
);
|
||||
},
|
||||
builder: (context, state) {
|
||||
if (state.currentWorkspace == null) {
|
||||
|
@ -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';
|
||||
@ -47,15 +47,6 @@ class MobileHomePageHeader extends StatelessWidget {
|
||||
: _MobileUser(userProfile: userProfile),
|
||||
),
|
||||
const HomePageSettingsPopupMenu(),
|
||||
// GestureDetector(
|
||||
// onTap: () => context.push(
|
||||
// MobileHomeSettingPage.routeName,
|
||||
// ),
|
||||
// child: const Padding(
|
||||
// padding: EdgeInsets.all(8.0),
|
||||
// child: FlowySvg(FlowySvgs.m_notification_settings_s),
|
||||
// ),
|
||||
// ),
|
||||
const HSpace(8.0),
|
||||
],
|
||||
),
|
||||
@ -118,6 +109,7 @@ class _MobileWorkspace extends StatelessWidget {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return AnimatedGestureDetector(
|
||||
scaleFactor: 0.99,
|
||||
alignment: Alignment.centerLeft,
|
||||
onTapUp: () {
|
||||
context.read<UserWorkspaceBloc>().add(
|
||||
|
@ -3,9 +3,11 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/presentation.dart';
|
||||
import 'package:appflowy/mobile/presentation/setting/workspace/invite_members_screen.dart';
|
||||
import 'package:appflowy/shared/popup_menu/appflowy_popup_menu.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart'
|
||||
hide PopupMenuButton, PopupMenuDivider, PopupMenuItem, PopupMenuEntry;
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
enum _MobileSettingsPopupMenuItem {
|
||||
@ -28,9 +30,9 @@ class HomePageSettingsPopupMenu extends StatelessWidget {
|
||||
Radius.circular(12.0),
|
||||
),
|
||||
),
|
||||
// todo: replace it with shadows
|
||||
shadowColor: const Color(0x68000000),
|
||||
elevation: 10,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: FlowySvg(
|
||||
|
@ -36,6 +36,7 @@ class EmptySpacePlaceholder extends StatelessWidget {
|
||||
lineHeight: 1.3,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
const VSpace(kBottomNavigationBarHeight + 36.0),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -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';
|
||||
|
@ -4,11 +4,13 @@ import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||
import 'package:appflowy/mobile/presentation/home/space/mobile_space_header.dart';
|
||||
import 'package:appflowy/mobile/presentation/home/space/mobile_space_menu.dart';
|
||||
import 'package:appflowy/mobile/presentation/page_item/mobile_view_item.dart';
|
||||
import 'package:appflowy/shared/list_extension.dart';
|
||||
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/home_sizes.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -124,8 +126,15 @@ class _Pages extends StatelessWidget {
|
||||
final spaceType = space.spacePermission == SpacePermission.publicToAll
|
||||
? FolderSpaceType.public
|
||||
: FolderSpaceType.private;
|
||||
final childViews = state.view.childViews.unique((view) => view.id);
|
||||
if (childViews.length != state.view.childViews.length) {
|
||||
final duplicatedViews = state.view.childViews
|
||||
.where((view) => childViews.contains(view))
|
||||
.toList();
|
||||
Log.error('some view id are duplicated: $duplicatedViews');
|
||||
}
|
||||
return Column(
|
||||
children: state.view.childViews
|
||||
children: childViews
|
||||
.map(
|
||||
(view) => MobileViewItem(
|
||||
key: ValueKey(
|
||||
|
@ -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';
|
||||
|
@ -1,12 +1,14 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/presentation.dart';
|
||||
import 'package:appflowy/shared/popup_menu/appflowy_popup_menu.dart';
|
||||
import 'package:appflowy/user/application/reminder/reminder_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart'
|
||||
hide PopupMenuButton, PopupMenuDivider, PopupMenuItem, PopupMenuEntry;
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
|
@ -16,13 +16,10 @@ class AppFlowyCloudPage extends StatelessWidget {
|
||||
appBar: FlowyAppBar(
|
||||
titleText: LocaleKeys.settings_menu_cloudSettings.tr(),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: SettingCloud(
|
||||
restartAppFlowy: () async {
|
||||
await runAppFlowy();
|
||||
},
|
||||
),
|
||||
body: SettingCloud(
|
||||
restartAppFlowy: () async {
|
||||
await runAppFlowy();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -33,7 +33,9 @@ class UserSessionSettingGroup extends StatelessWidget {
|
||||
);
|
||||
},
|
||||
builder: (context, state) {
|
||||
return const ThirdPartySignInButtons();
|
||||
return const ThirdPartySignInButtons(
|
||||
expanded: true,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
@ -34,6 +34,7 @@ class InviteMembersScreen extends StatelessWidget {
|
||||
titleText: LocaleKeys.settings_appearance_members_label.tr(),
|
||||
),
|
||||
body: const _InviteMemberPage(),
|
||||
resizeToAvoidBottomInset: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -193,6 +194,9 @@ class _InviteMemberPageState extends State<_InviteMemberPage> {
|
||||
final actionType = actionResult.actionType;
|
||||
final result = actionResult.result;
|
||||
|
||||
// get keyboard height
|
||||
final keyboardHeight = MediaQuery.of(context).viewInsets.bottom;
|
||||
|
||||
// only show the result dialog when the action is WorkspaceMemberActionType.add
|
||||
if (actionType == WorkspaceMemberActionType.add) {
|
||||
result.fold(
|
||||
@ -201,6 +205,7 @@ class _InviteMemberPageState extends State<_InviteMemberPage> {
|
||||
context,
|
||||
message:
|
||||
LocaleKeys.settings_appearance_members_addMemberSuccess.tr(),
|
||||
bottomPadding: keyboardHeight,
|
||||
);
|
||||
},
|
||||
(f) {
|
||||
@ -216,6 +221,7 @@ class _InviteMemberPageState extends State<_InviteMemberPage> {
|
||||
showToastNotification(
|
||||
context,
|
||||
type: ToastificationType.error,
|
||||
bottomPadding: keyboardHeight,
|
||||
message: message,
|
||||
);
|
||||
},
|
||||
@ -227,6 +233,7 @@ class _InviteMemberPageState extends State<_InviteMemberPage> {
|
||||
context,
|
||||
message:
|
||||
LocaleKeys.settings_appearance_members_inviteMemberSuccess.tr(),
|
||||
bottomPadding: keyboardHeight,
|
||||
);
|
||||
},
|
||||
(f) {
|
||||
@ -244,6 +251,7 @@ class _InviteMemberPageState extends State<_InviteMemberPage> {
|
||||
context,
|
||||
type: ToastificationType.error,
|
||||
message: message,
|
||||
bottomPadding: keyboardHeight,
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -255,6 +263,7 @@ class _InviteMemberPageState extends State<_InviteMemberPage> {
|
||||
message: LocaleKeys
|
||||
.settings_appearance_members_removeFromWorkspaceSuccess
|
||||
.tr(),
|
||||
bottomPadding: keyboardHeight,
|
||||
);
|
||||
},
|
||||
(f) {
|
||||
@ -264,6 +273,7 @@ class _InviteMemberPageState extends State<_InviteMemberPage> {
|
||||
message: LocaleKeys
|
||||
.settings_appearance_members_removeFromWorkspaceFailed
|
||||
.tr(),
|
||||
bottomPadding: keyboardHeight,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -89,6 +89,15 @@ class CellController<T, D> {
|
||||
fieldId: _cellContext.fieldId,
|
||||
);
|
||||
|
||||
_rowCache.addListener(
|
||||
rowId: rowId,
|
||||
onRowChanged: (context, reason) {
|
||||
if (reason == const ChangedReason.didFetchRow()) {
|
||||
_onRowMetaChanged?.call();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// 1. Listen on user edit event and load the new cell data if needed.
|
||||
// For example:
|
||||
// user input: 12
|
||||
|
@ -44,7 +44,8 @@ class RowCache {
|
||||
for (final fieldInfo in fieldInfos) {
|
||||
_cellMemCache.removeCellWithFieldId(fieldInfo.id);
|
||||
}
|
||||
_changedNotifier.receive(const ChangedReason.fieldDidChange());
|
||||
|
||||
_changedNotifier?.receive(const ChangedReason.fieldDidChange());
|
||||
});
|
||||
}
|
||||
|
||||
@ -53,7 +54,9 @@ class RowCache {
|
||||
final CellMemCache _cellMemCache;
|
||||
final RowLifeCycle _rowLifeCycle;
|
||||
final RowFieldsDelegate _fieldDelegate;
|
||||
final RowChangesetNotifier _changedNotifier;
|
||||
RowChangesetNotifier? _changedNotifier;
|
||||
bool _isInitialRows = false;
|
||||
final List<RowsVisibilityChangePB> _pendingVisibilityChanges = [];
|
||||
|
||||
/// Returns a unmodifiable list of RowInfo
|
||||
UnmodifiableListView<RowInfo> get rowInfos {
|
||||
@ -67,7 +70,8 @@ class RowCache {
|
||||
}
|
||||
|
||||
CellMemCache get cellCache => _cellMemCache;
|
||||
ChangedReason get changeReason => _changedNotifier.reason;
|
||||
ChangedReason get changeReason =>
|
||||
_changedNotifier?.reason ?? const InitialListState();
|
||||
|
||||
RowInfo? getRow(RowId rowId) {
|
||||
return _rowList.get(rowId);
|
||||
@ -78,18 +82,25 @@ class RowCache {
|
||||
final rowInfo = buildGridRow(row);
|
||||
_rowList.add(rowInfo);
|
||||
}
|
||||
_changedNotifier.receive(const ChangedReason.setInitialRows());
|
||||
_isInitialRows = true;
|
||||
_changedNotifier?.receive(const ChangedReason.setInitialRows());
|
||||
|
||||
for (final changeset in _pendingVisibilityChanges) {
|
||||
applyRowsVisibility(changeset);
|
||||
}
|
||||
_pendingVisibilityChanges.clear();
|
||||
}
|
||||
|
||||
void setRowMeta(RowMetaPB rowMeta) {
|
||||
final rowInfo = buildGridRow(rowMeta);
|
||||
_rowList.add(rowInfo);
|
||||
_changedNotifier.receive(const ChangedReason.didFetchRow());
|
||||
_changedNotifier?.receive(const ChangedReason.didFetchRow());
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_rowLifeCycle.onRowDisposed();
|
||||
_changedNotifier.dispose();
|
||||
_changedNotifier?.dispose();
|
||||
_changedNotifier = null;
|
||||
_cellMemCache.dispose();
|
||||
}
|
||||
|
||||
@ -100,13 +111,17 @@ 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) {
|
||||
_rowList.reorderWithRowIds(rowIds);
|
||||
_changedNotifier.receive(const ChangedReason.reorderRows());
|
||||
_changedNotifier?.receive(const ChangedReason.reorderRows());
|
||||
}
|
||||
|
||||
void reorderSingleRow(ReorderSingleRowPB reorderRow) {
|
||||
@ -117,7 +132,7 @@ class RowCache {
|
||||
reorderRow.oldIndex,
|
||||
reorderRow.newIndex,
|
||||
);
|
||||
_changedNotifier.receive(
|
||||
_changedNotifier?.receive(
|
||||
ChangedReason.reorderSingleRow(
|
||||
reorderRow,
|
||||
rowInfo,
|
||||
@ -130,7 +145,7 @@ class RowCache {
|
||||
for (final rowId in deletedRowIds) {
|
||||
final deletedRow = _rowList.remove(rowId);
|
||||
if (deletedRow != null) {
|
||||
_changedNotifier.receive(ChangedReason.delete(deletedRow));
|
||||
_changedNotifier?.receive(ChangedReason.delete(deletedRow));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -140,7 +155,7 @@ class RowCache {
|
||||
final insertedIndex =
|
||||
_rowList.insert(insertedRow.index, buildGridRow(insertedRow.rowMeta));
|
||||
if (insertedIndex != null) {
|
||||
_changedNotifier.receive(ChangedReason.insert(insertedIndex));
|
||||
_changedNotifier?.receive(ChangedReason.insert(insertedIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -165,7 +180,7 @@ class RowCache {
|
||||
_rowList.updateRows(updatedList, (rowId) => buildGridRow(rowId));
|
||||
|
||||
if (updatedIndexs.isNotEmpty) {
|
||||
_changedNotifier.receive(ChangedReason.update(updatedIndexs));
|
||||
_changedNotifier?.receive(ChangedReason.update(updatedIndexs));
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,7 +188,7 @@ class RowCache {
|
||||
for (final rowId in invisibleRows) {
|
||||
final deletedRow = _rowList.remove(rowId);
|
||||
if (deletedRow != null) {
|
||||
_changedNotifier.receive(ChangedReason.delete(deletedRow));
|
||||
_changedNotifier?.receive(ChangedReason.delete(deletedRow));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -183,14 +198,16 @@ class RowCache {
|
||||
final insertedIndex =
|
||||
_rowList.insert(insertedRow.index, buildGridRow(insertedRow.rowMeta));
|
||||
if (insertedIndex != null) {
|
||||
_changedNotifier.receive(ChangedReason.insert(insertedIndex));
|
||||
_changedNotifier?.receive(ChangedReason.insert(insertedIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onRowsChanged(void Function(ChangedReason) onRowChanged) {
|
||||
_changedNotifier.addListener(() {
|
||||
onRowChanged(_changedNotifier.reason);
|
||||
_changedNotifier?.addListener(() {
|
||||
if (_changedNotifier != null) {
|
||||
onRowChanged(_changedNotifier!.reason);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -203,17 +220,19 @@ class RowCache {
|
||||
final rowInfo = _rowList.get(rowId);
|
||||
if (rowInfo != null) {
|
||||
final cellDataMap = _makeCells(rowInfo.rowMeta);
|
||||
onRowChanged(cellDataMap, _changedNotifier.reason);
|
||||
if (_changedNotifier != null) {
|
||||
onRowChanged(cellDataMap, _changedNotifier!.reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_changedNotifier.addListener(listenerHandler);
|
||||
_changedNotifier?.addListener(listenerHandler);
|
||||
return listenerHandler;
|
||||
}
|
||||
|
||||
void removeRowListener(VoidCallback callback) {
|
||||
_changedNotifier.removeListener(callback);
|
||||
_changedNotifier?.removeListener(callback);
|
||||
}
|
||||
|
||||
List<CellContext> loadCells(RowMetaPB rowMeta) {
|
||||
@ -242,7 +261,7 @@ class RowCache {
|
||||
rowId: rowMetaPB.id,
|
||||
);
|
||||
|
||||
_changedNotifier.receive(ChangedReason.update(updatedIndexs));
|
||||
_changedNotifier?.receive(ChangedReason.update(updatedIndexs));
|
||||
}
|
||||
},
|
||||
(err) => Log.error(err),
|
||||
|
@ -1,6 +1,8 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:appflowy/plugins/database/application/row/row_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/row_listener.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/row_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../cell/cell_cache.dart';
|
||||
@ -18,21 +20,11 @@ class RowController {
|
||||
}) : _rowMeta = rowMeta,
|
||||
_rowCache = rowCache,
|
||||
_rowBackendSvc = RowBackendService(viewId: viewId),
|
||||
_rowListener = RowListener(rowMeta.id) {
|
||||
_rowBackendSvc.initRow(rowMeta.id);
|
||||
_rowListener.start(
|
||||
onMetaChanged: (newRowMeta) {
|
||||
if (_isDisposed) {
|
||||
return;
|
||||
}
|
||||
_rowMeta = newRowMeta;
|
||||
_rowCache.setRowMeta(newRowMeta);
|
||||
},
|
||||
);
|
||||
}
|
||||
_rowListener = RowListener(rowMeta.id);
|
||||
|
||||
RowMetaPB _rowMeta;
|
||||
final String? groupId;
|
||||
VoidCallback? _onRowMetaChanged;
|
||||
final String viewId;
|
||||
final List<VoidCallback> _onRowChangedListeners = [];
|
||||
final RowCache _rowCache;
|
||||
@ -40,14 +32,52 @@ class RowController {
|
||||
final RowBackendService _rowBackendSvc;
|
||||
bool _isDisposed = false;
|
||||
|
||||
CellMemCache get cellCache => _rowCache.cellCache;
|
||||
|
||||
String get rowId => rowMeta.id;
|
||||
RowMetaPB get rowMeta => _rowMeta;
|
||||
CellMemCache get cellCache => _rowCache.cellCache;
|
||||
|
||||
List<CellContext> loadCells() => _rowCache.loadCells(rowMeta);
|
||||
|
||||
void addListener({OnRowChanged? onRowChanged}) {
|
||||
Future<void> initialize() async {
|
||||
await _rowBackendSvc.initRow(rowMeta.id);
|
||||
unawaited(
|
||||
_rowBackendSvc.getRowMeta(rowId).then(
|
||||
(result) {
|
||||
if (_isDisposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
result.fold(
|
||||
(rowMeta) {
|
||||
_rowMeta = rowMeta;
|
||||
_rowCache.setRowMeta(rowMeta);
|
||||
_onRowMetaChanged?.call();
|
||||
},
|
||||
(error) => debugPrint(error.toString()),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
_rowListener.start(
|
||||
onRowFetched: (DidFetchRowPB row) {
|
||||
_rowCache.setRowMeta(row.meta);
|
||||
},
|
||||
onMetaChanged: (newRowMeta) {
|
||||
if (_isDisposed) {
|
||||
return;
|
||||
}
|
||||
_rowMeta = newRowMeta;
|
||||
_rowCache.setRowMeta(newRowMeta);
|
||||
_onRowMetaChanged?.call();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void addListener({
|
||||
OnRowChanged? onRowChanged,
|
||||
VoidCallback? onMetaChanged,
|
||||
}) {
|
||||
final fn = _rowCache.addListener(
|
||||
rowId: rowMeta.id,
|
||||
onRowChanged: (context, reasons) {
|
||||
@ -60,6 +90,7 @@ class RowController {
|
||||
|
||||
// Add the listener to the list so that we can remove it later.
|
||||
_onRowChangedListeners.add(fn);
|
||||
_onRowMetaChanged = onMetaChanged;
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
|
@ -2,9 +2,7 @@ import 'dart:async';
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:appflowy/plugins/database/application/row/row_service.dart';
|
||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
|
||||
import '../defines.dart';
|
||||
import '../field/field_controller.dart';
|
||||
@ -93,17 +91,6 @@ class DatabaseViewCache {
|
||||
(reorderRow) => _rowCache.reorderSingleRow(reorderRow),
|
||||
(err) => Log.error(err),
|
||||
),
|
||||
onReloadRows: () {
|
||||
final payload = DatabaseViewIdPB(value: viewId);
|
||||
DatabaseEventGetAllRows(payload).send().then((result) {
|
||||
result.fold(
|
||||
(rows) {
|
||||
_rowCache.setInitialRows(rows.items);
|
||||
},
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
_rowCache.onRowsChanged(
|
||||
|
@ -32,7 +32,6 @@ class DatabaseViewListener {
|
||||
required ReorderAllRowsCallback onReorderAllRows,
|
||||
required SingleRowCallback onReorderSingleRow,
|
||||
required RowsVisibilityCallback onRowsVisibilityChanged,
|
||||
required void Function() onReloadRows,
|
||||
}) {
|
||||
// Stop any existing listener
|
||||
_listener?.stop();
|
||||
@ -47,7 +46,6 @@ class DatabaseViewListener {
|
||||
onReorderAllRows,
|
||||
onReorderSingleRow,
|
||||
onRowsVisibilityChanged,
|
||||
onReloadRows,
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -59,7 +57,6 @@ class DatabaseViewListener {
|
||||
ReorderAllRowsCallback onReorderAllRows,
|
||||
SingleRowCallback onReorderSingleRow,
|
||||
RowsVisibilityCallback onRowsVisibilityChanged,
|
||||
void Function() onReloadRows,
|
||||
) {
|
||||
switch (ty) {
|
||||
case DatabaseNotification.DidUpdateViewRowsVisibility:
|
||||
@ -94,9 +91,6 @@ class DatabaseViewListener {
|
||||
(error) => onReorderSingleRow(FlowyResult.failure(error)),
|
||||
);
|
||||
break;
|
||||
case DatabaseNotification.ReloadRows:
|
||||
onReloadRows();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -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;
|
||||
@ -653,7 +651,7 @@ class _BoardCardState extends State<_BoardCard> {
|
||||
onTap: (context) => _openCard(
|
||||
context: context,
|
||||
databaseController: databaseController,
|
||||
rowMeta: context.read<CardBloc>().state.rowMeta,
|
||||
rowMeta: context.read<CardBloc>().rowController.rowMeta,
|
||||
),
|
||||
onShiftTap: (_) {
|
||||
Focus.of(context).requestFocus();
|
||||
|
@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ class RowBloc extends Bloc<RowEvent, RowState> {
|
||||
_dispatch();
|
||||
_startListening();
|
||||
_init();
|
||||
rowController.initialize();
|
||||
}
|
||||
|
||||
final FieldController fieldController;
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/application/row/row_service.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculations_row.dart';
|
||||
@ -305,22 +307,26 @@ class _GridRowsState extends State<_GridRows> {
|
||||
buildWhen: (previous, current) => previous.fields != current.fields,
|
||||
builder: (context, state) {
|
||||
return Flexible(
|
||||
child: _WrapScrollView(
|
||||
scrollController: widget.scrollController,
|
||||
contentWidth: GridLayout.headerWidth(state.fields),
|
||||
child: BlocConsumer<GridBloc, GridState>(
|
||||
listenWhen: (previous, current) =>
|
||||
previous.rowCount != current.rowCount,
|
||||
listener: (context, state) => _evaluateFloatingCalculations(),
|
||||
builder: (context, state) {
|
||||
return ScrollConfiguration(
|
||||
behavior: ScrollConfiguration.of(context).copyWith(
|
||||
scrollbars: false,
|
||||
),
|
||||
child: _renderList(context, state),
|
||||
);
|
||||
},
|
||||
),
|
||||
child: LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints layoutConstraits) {
|
||||
return _WrapScrollView(
|
||||
scrollController: widget.scrollController,
|
||||
contentWidth: GridLayout.headerWidth(state.fields),
|
||||
child: BlocConsumer<GridBloc, GridState>(
|
||||
listenWhen: (previous, current) =>
|
||||
previous.rowCount != current.rowCount,
|
||||
listener: (context, state) => _evaluateFloatingCalculations(),
|
||||
builder: (context, state) {
|
||||
return ScrollConfiguration(
|
||||
behavior: ScrollConfiguration.of(context).copyWith(
|
||||
scrollbars: false,
|
||||
),
|
||||
child: _renderList(context, state, layoutConstraits),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
@ -330,19 +336,19 @@ class _GridRowsState extends State<_GridRows> {
|
||||
Widget _renderList(
|
||||
BuildContext context,
|
||||
GridState state,
|
||||
BoxConstraints layoutConstraints,
|
||||
) {
|
||||
// 1. GridRowBottomBar
|
||||
// 2. GridCalculationsRow
|
||||
// 3. Footer Padding
|
||||
final itemCount = state.rowInfos.length + 3;
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: ReorderableListView.builder(
|
||||
/// This is a workaround related to
|
||||
/// https://github.com/flutter/flutter/issues/25652
|
||||
cacheExtent: 5000,
|
||||
cacheExtent: max(layoutConstraints.maxHeight * 2, 500),
|
||||
scrollController: widget.scrollController.verticalController,
|
||||
physics: const ClampingScrollPhysics(),
|
||||
buildDefaultDragHandles: false,
|
||||
@ -421,7 +427,7 @@ class _GridRowsState extends State<_GridRows> {
|
||||
);
|
||||
|
||||
final child = GridRow(
|
||||
key: ValueKey(rowMeta.id),
|
||||
key: ValueKey(rowId),
|
||||
fieldController: databaseController.fieldController,
|
||||
rowId: rowId,
|
||||
viewId: viewId,
|
||||
|
@ -1,5 +1,3 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/database/card/card_detail/mobile_card_detail_screen.dart';
|
||||
@ -9,6 +7,7 @@ import 'package:appflowy/plugins/database/application/row/row_service.dart';
|
||||
import 'package:appflowy/plugins/database/grid/application/grid_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/shortcuts.dart';
|
||||
import 'package:appflowy/plugins/database/tab_bar/tab_bar_view.dart';
|
||||
import 'package:appflowy/shared/flowy_error_page.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/application/action_navigation/action_navigation_bloc.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
@ -18,7 +17,7 @@ import 'package:collection/collection.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:flowy_infra_ui/widget/error_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:linked_scroll_controller/linked_scroll_controller.dart';
|
||||
@ -106,9 +105,8 @@ class _MobileGridPageState extends State<MobileGridPage> {
|
||||
_openRow(context, widget.initialRowId, true);
|
||||
return result.successOrFail.fold(
|
||||
(_) => GridShortcuts(child: GridPageContent(view: widget.view)),
|
||||
(err) => FlowyErrorPage.message(
|
||||
err.toString(),
|
||||
howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),
|
||||
(err) => AppFlowyErrorPage(
|
||||
error: err,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -1,5 +1,3 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/plugins/database/application/cell/cell_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||
@ -9,6 +7,7 @@ import 'package:appflowy/plugins/database/application/row/row_service.dart';
|
||||
import 'package:appflowy/plugins/database/grid/application/row/row_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_builder.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/cells/mobile_cell_container.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../layout/sizes.dart';
|
||||
@ -81,8 +80,8 @@ class _MobileGridRowState extends State<MobileGridRow> {
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
await _rowController.dispose();
|
||||
super.dispose();
|
||||
await _rowController.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ class _DatabaseViewSettingContent extends StatelessWidget {
|
||||
builder: (context, state) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: GridSize.horizontalHeaderPadding + 40,
|
||||
horizontal: GridSize.horizontalHeaderPadding,
|
||||
),
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
|
@ -2,6 +2,7 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/database/card/card.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/row/row_cache.dart';
|
||||
import 'package:appflowy/plugins/database/application/row/row_controller.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/row/action.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/row_entities.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
@ -73,13 +74,18 @@ class _RowCardState extends State<RowCard> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final rowController = RowController(
|
||||
viewId: widget.viewId,
|
||||
rowMeta: widget.rowMeta,
|
||||
rowCache: widget.rowCache,
|
||||
);
|
||||
|
||||
_cardBloc = CardBloc(
|
||||
fieldController: widget.fieldController,
|
||||
viewId: widget.viewId,
|
||||
groupFieldId: widget.groupingFieldId,
|
||||
isEditing: widget.isEditing,
|
||||
rowMeta: widget.rowMeta,
|
||||
rowCache: widget.rowCache,
|
||||
rowController: rowController,
|
||||
)..add(const CardEvent.initial());
|
||||
}
|
||||
|
||||
@ -143,7 +149,7 @@ class _RowCardState extends State<RowCard> {
|
||||
popupBuilder: (_) {
|
||||
return RowActionMenu.board(
|
||||
viewId: _cardBloc.viewId,
|
||||
rowId: _cardBloc.rowId,
|
||||
rowId: _cardBloc.rowController.rowId,
|
||||
groupId: widget.groupId,
|
||||
);
|
||||
},
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:appflowy/plugins/database/application/row/row_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/row/row_service.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
@ -7,7 +8,6 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/cell_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/row/row_cache.dart';
|
||||
import 'package:appflowy/plugins/database/domain/row_listener.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/setting/field_visibility_extension.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
@ -19,42 +19,35 @@ class CardBloc extends Bloc<CardEvent, CardState> {
|
||||
required this.fieldController,
|
||||
required this.groupFieldId,
|
||||
required this.viewId,
|
||||
required RowMetaPB rowMeta,
|
||||
required RowCache rowCache,
|
||||
required bool isEditing,
|
||||
}) : rowId = rowMeta.id,
|
||||
_rowListener = RowListener(rowMeta.id),
|
||||
_rowCache = rowCache,
|
||||
super(
|
||||
required this.rowController,
|
||||
}) : super(
|
||||
CardState.initial(
|
||||
rowMeta,
|
||||
_makeCells(
|
||||
fieldController,
|
||||
groupFieldId,
|
||||
rowCache.loadCells(rowMeta),
|
||||
rowController,
|
||||
),
|
||||
isEditing,
|
||||
rowController.rowMeta,
|
||||
),
|
||||
) {
|
||||
_dispatch();
|
||||
}
|
||||
|
||||
final FieldController fieldController;
|
||||
final String rowId;
|
||||
final String? groupFieldId;
|
||||
final RowCache _rowCache;
|
||||
final String viewId;
|
||||
final RowListener _rowListener;
|
||||
final RowController rowController;
|
||||
|
||||
VoidCallback? _rowCallback;
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
if (_rowCallback != null) {
|
||||
_rowCache.removeRowListener(_rowCallback!);
|
||||
_rowCallback = null;
|
||||
}
|
||||
await _rowListener.stop();
|
||||
await rowController.dispose();
|
||||
return super.close();
|
||||
}
|
||||
|
||||
@ -85,20 +78,17 @@ class CardBloc extends Bloc<CardEvent, CardState> {
|
||||
}
|
||||
|
||||
Future<void> _startListening() async {
|
||||
_rowCallback = _rowCache.addListener(
|
||||
rowId: rowId,
|
||||
rowController.addListener(
|
||||
onRowChanged: (cellMap, reason) {
|
||||
if (!isClosed) {
|
||||
final cells = _makeCells(fieldController, groupFieldId, cellMap);
|
||||
final cells =
|
||||
_makeCells(fieldController, groupFieldId, rowController);
|
||||
add(CardEvent.didReceiveCells(cells, reason));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
_rowListener.start(
|
||||
onMetaChanged: (rowMeta) {
|
||||
onMetaChanged: () {
|
||||
if (!isClosed) {
|
||||
add(CardEvent.didUpdateRowMeta(rowMeta));
|
||||
add(CardEvent.didUpdateRowMeta(rowController.rowMeta));
|
||||
}
|
||||
},
|
||||
);
|
||||
@ -108,16 +98,18 @@ class CardBloc extends Bloc<CardEvent, CardState> {
|
||||
List<CellMeta> _makeCells(
|
||||
FieldController fieldController,
|
||||
String? groupFieldId,
|
||||
List<CellContext> cellContexts,
|
||||
RowController rowController,
|
||||
) {
|
||||
// Only show the non-hidden cells and cells that aren't of the grouping field
|
||||
cellContexts.removeWhere((cellContext) {
|
||||
final cellContext = rowController.loadCells();
|
||||
|
||||
cellContext.removeWhere((cellContext) {
|
||||
final fieldInfo = fieldController.getField(cellContext.fieldId);
|
||||
return fieldInfo == null ||
|
||||
!(fieldInfo.visibility?.isVisibleState() ?? false) ||
|
||||
(groupFieldId != null && cellContext.fieldId == groupFieldId);
|
||||
});
|
||||
return cellContexts
|
||||
return cellContext
|
||||
.map(
|
||||
(cellCtx) => CellMeta(
|
||||
fieldId: cellCtx.fieldId,
|
||||
@ -157,19 +149,19 @@ class CellMeta with _$CellMeta {
|
||||
class CardState with _$CardState {
|
||||
const factory CardState({
|
||||
required List<CellMeta> cells,
|
||||
required RowMetaPB rowMeta,
|
||||
required bool isEditing,
|
||||
required RowMetaPB rowMeta,
|
||||
ChangedReason? changeReason,
|
||||
}) = _RowCardState;
|
||||
|
||||
factory CardState.initial(
|
||||
RowMetaPB rowMeta,
|
||||
List<CellMeta> cells,
|
||||
bool isEditing,
|
||||
RowMetaPB rowMeta,
|
||||
) =>
|
||||
CardState(
|
||||
cells: cells,
|
||||
rowMeta: rowMeta,
|
||||
isEditing: isEditing,
|
||||
rowMeta: rowMeta,
|
||||
);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import 'package:appflowy/workspace/presentation/home/toast.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@ -202,8 +203,14 @@ class _URLAccessoryIconContainer extends StatelessWidget {
|
||||
),
|
||||
borderRadius: Corners.s6Border,
|
||||
),
|
||||
child: Center(
|
||||
child: child,
|
||||
child: FlowyHover(
|
||||
style: HoverStyle(
|
||||
backgroundColor: AFThemeExtension.of(context).background,
|
||||
hoverColor: AFThemeExtension.of(context).lightGreyHover,
|
||||
),
|
||||
child: Center(
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/bloc/text_cell_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@ -17,27 +17,28 @@ class MobileGridTextCellSkin extends IEditableTextCellSkin {
|
||||
) {
|
||||
return Row(
|
||||
children: [
|
||||
const HSpace(10),
|
||||
BlocBuilder<TextCellBloc, TextCellState>(
|
||||
buildWhen: (p, c) => p.emoji != c.emoji,
|
||||
builder: (context, state) => Center(
|
||||
child: FlowyText(
|
||||
child: FlowyText.emoji(
|
||||
state.emoji,
|
||||
fontSize: 16,
|
||||
fontSize: 15,
|
||||
optimizeEmojiAlign: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
const HSpace(6),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: textEditingController,
|
||||
focusNode: focusNode,
|
||||
style:
|
||||
Theme.of(context).textTheme.bodyMedium?.copyWith(fontSize: 15),
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 15,
|
||||
),
|
||||
decoration: const InputDecoration(
|
||||
enabledBorder: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: 14, vertical: 12),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 4),
|
||||
isCollapsed: true,
|
||||
),
|
||||
onTapOutside: (event) => focusNode.unfocus(),
|
||||
|
@ -372,7 +372,7 @@ class _NewTaskItemState extends State<NewTaskItem> {
|
||||
? Theme.of(context).disabledColor
|
||||
: Theme.of(context).colorScheme.primaryContainer,
|
||||
fontColor: Theme.of(context).colorScheme.onPrimary,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
||||
onPressed: _textEditingController.text.isEmpty
|
||||
? null
|
||||
: () {
|
||||
|
@ -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/shared/flowy_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: AppFlowyErrorPage(
|
||||
error: error,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -499,11 +499,12 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
}
|
||||
|
||||
void _customizeBlockComponentBackgroundColorDecorator() {
|
||||
if (!context.mounted) {
|
||||
return;
|
||||
}
|
||||
blockComponentBackgroundColorDecorator = (Node node, String colorString) =>
|
||||
buildEditorCustomizedColor(context, node, colorString);
|
||||
blockComponentBackgroundColorDecorator = (Node node, String colorString) {
|
||||
if (mounted && context.mounted) {
|
||||
return buildEditorCustomizedColor(context, node, colorString);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
void _initEditorL10n() => AppFlowyEditorL10n.current = EditorI18n();
|
||||
|
@ -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),
|
||||
),
|
||||
|
@ -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',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
168
frontend/appflowy_flutter/lib/shared/flowy_error_page.dart
Normal file
@ -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 AppFlowyErrorPage extends StatelessWidget {
|
||||
const AppFlowyErrorPage({
|
||||
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,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -121,6 +121,8 @@ extension ProviderTypePBExtension on ProviderTypePB {
|
||||
return ProviderTypePB.Google;
|
||||
case 'discord':
|
||||
return ProviderTypePB.Discord;
|
||||
case 'apple':
|
||||
return ProviderTypePB.Apple;
|
||||
default:
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/startup/tasks/appflowy_cloud_task.dart';
|
||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/code.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
|
||||
show UserProfilePB;
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
@ -208,6 +208,8 @@ class SignInBloc extends Bloc<SignInEvent, SignInState> {
|
||||
}
|
||||
|
||||
SignInState _stateFromCode(FlowyError error) {
|
||||
Log.error('SignInState _stateFromCode: ${error.msg}');
|
||||
|
||||
switch (error.code) {
|
||||
case ErrorCode.EmailFormatInvalid:
|
||||
return state.copyWith(
|
||||
|
@ -1,11 +1,12 @@
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||
import 'package:appflowy/user/presentation/router.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/workspace.pb.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/snap_bar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:toastification/toastification.dart';
|
||||
|
||||
void handleOpenWorkspaceError(BuildContext context, FlowyError error) {
|
||||
Log.error(error);
|
||||
@ -15,24 +16,24 @@ void handleOpenWorkspaceError(BuildContext context, FlowyError error) {
|
||||
getIt<AuthRouter>().pushWorkspaceErrorScreen(context, userFolder, error);
|
||||
break;
|
||||
case ErrorCode.InvalidEncryptSecret:
|
||||
showSnapBar(
|
||||
case ErrorCode.HttpError:
|
||||
showToastNotification(
|
||||
context,
|
||||
error.msg,
|
||||
message: error.msg,
|
||||
type: ToastificationType.error,
|
||||
);
|
||||
break;
|
||||
case ErrorCode.HttpError:
|
||||
showSnapBar(
|
||||
context,
|
||||
error.msg,
|
||||
);
|
||||
default:
|
||||
showSnapBar(
|
||||
showToastNotification(
|
||||
context,
|
||||
error.msg,
|
||||
onClosed: () {
|
||||
getIt<AuthService>().signOut();
|
||||
runAppFlowy();
|
||||
},
|
||||
message: error.msg,
|
||||
type: ToastificationType.error,
|
||||
callbacks: ToastificationCallbacks(
|
||||
onDismissed: (_) {
|
||||
getIt<AuthService>().signOut();
|
||||
runAppFlowy();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/core/frameless_window.dart';
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
@ -10,6 +8,7 @@ import 'package:appflowy/user/presentation/widgets/widgets.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class DesktopSignInScreen extends StatelessWidget {
|
||||
@ -45,14 +44,14 @@ class DesktopSignInScreen extends StatelessWidget {
|
||||
|
||||
if (isAuthEnabled) ...[
|
||||
const _OrDivider(),
|
||||
const VSpace(10),
|
||||
const VSpace(20),
|
||||
const ThirdPartySignInButtons(),
|
||||
],
|
||||
const VSpace(20),
|
||||
|
||||
// anonymous sign in
|
||||
const SignInAnonymousButtonV2(),
|
||||
const VSpace(10),
|
||||
const VSpace(16),
|
||||
|
||||
// sign in agreement
|
||||
const SignInAgreement(),
|
||||
|
@ -1,4 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
@ -8,6 +8,7 @@ import 'package:appflowy/user/application/sign_in_bloc.dart';
|
||||
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
@ -23,26 +24,27 @@ class MobileSignInScreen extends StatelessWidget {
|
||||
return BlocBuilder<SignInBloc, SignInState>(
|
||||
builder: (context, state) {
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 50, horizontal: 40),
|
||||
padding: const EdgeInsets.symmetric(vertical: 38, horizontal: 40),
|
||||
child: Column(
|
||||
children: [
|
||||
const Spacer(flex: 4),
|
||||
_buildLogo(),
|
||||
const VSpace(spacing * 2),
|
||||
_buildWelcomeText(),
|
||||
const VSpace(spacing),
|
||||
_buildAppNameText(colorScheme),
|
||||
const VSpace(spacing * 2),
|
||||
const SignInWithMagicLinkButtons(),
|
||||
const VSpace(spacing),
|
||||
if (isAuthEnabled) _buildThirdPartySignInButtons(colorScheme),
|
||||
const VSpace(spacing),
|
||||
const SignInAnonymousButtonV2(),
|
||||
const VSpace(spacing),
|
||||
const VSpace(spacing * 1.5),
|
||||
const SignInAgreement(),
|
||||
const VSpace(spacing),
|
||||
_buildSettingsButton(context),
|
||||
if (!isAuthEnabled) const Spacer(flex: 2),
|
||||
const Spacer(flex: 2),
|
||||
const Spacer(),
|
||||
Expanded(child: _buildSettingsButton(context)),
|
||||
if (Platform.isAndroid) const Spacer(),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -51,19 +53,10 @@ class MobileSignInScreen extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildWelcomeText() {
|
||||
return FlowyText(
|
||||
LocaleKeys.welcomeTo.tr(),
|
||||
textAlign: TextAlign.center,
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.w700,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLogo() {
|
||||
return const FlowySvg(
|
||||
FlowySvgs.flowy_logo_xl,
|
||||
size: Size.square(64),
|
||||
size: Size.square(56),
|
||||
blendMode: null,
|
||||
);
|
||||
}
|
||||
@ -72,7 +65,7 @@ class MobileSignInScreen extends StatelessWidget {
|
||||
return FlowyText(
|
||||
LocaleKeys.appName.tr(),
|
||||
textAlign: TextAlign.center,
|
||||
fontSize: 32,
|
||||
fontSize: 28,
|
||||
color: const Color(0xFF00BCF0),
|
||||
fontWeight: FontWeight.w700,
|
||||
);
|
||||
@ -89,6 +82,7 @@ class MobileSignInScreen extends StatelessWidget {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: FlowyText(
|
||||
LocaleKeys.signIn_or.tr(),
|
||||
fontSize: 12,
|
||||
color: colorScheme.onSecondary,
|
||||
),
|
||||
),
|
||||
@ -96,23 +90,45 @@ class MobileSignInScreen extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
const VSpace(16),
|
||||
const ThirdPartySignInButtons(),
|
||||
// expand third-party sign in buttons on Android by default.
|
||||
// on iOS, the github and discord buttons are collapsed by default.
|
||||
ThirdPartySignInButtons(
|
||||
expanded: Platform.isAndroid,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSettingsButton(BuildContext context) {
|
||||
return FlowyButton(
|
||||
text: FlowyText(
|
||||
LocaleKeys.signIn_settings.tr(),
|
||||
textAlign: TextAlign.center,
|
||||
fontSize: 12.0,
|
||||
fontWeight: FontWeight.w500,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
onTap: () {
|
||||
context.push(MobileLaunchSettingsPage.routeName);
|
||||
},
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
FlowyButton(
|
||||
useIntrinsicWidth: true,
|
||||
text: FlowyText(
|
||||
LocaleKeys.signIn_settings.tr(),
|
||||
textAlign: TextAlign.center,
|
||||
fontSize: 12.0,
|
||||
// fontWeight: FontWeight.w500,
|
||||
color: Colors.grey,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
onTap: () {
|
||||
context.push(MobileLaunchSettingsPage.routeName);
|
||||
},
|
||||
),
|
||||
const HSpace(24),
|
||||
SignInAnonymousButtonV2(
|
||||
child: FlowyText(
|
||||
LocaleKeys.signIn_anonymous.tr(),
|
||||
textAlign: TextAlign.center,
|
||||
fontSize: 12.0,
|
||||
// fontWeight: FontWeight.w500,
|
||||
color: Colors.grey,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/application/sign_in_bloc.dart';
|
||||
import 'package:appflowy/user/presentation/router.dart';
|
||||
@ -7,6 +5,7 @@ import 'package:appflowy/user/presentation/screens/sign_in_screen/desktop_sign_i
|
||||
import 'package:appflowy/user/presentation/screens/sign_in_screen/mobile_loading_screen.dart';
|
||||
import 'package:appflowy/user/presentation/screens/sign_in_screen/mobile_sign_in_screen.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../helpers/helpers.dart';
|
||||
@ -21,16 +20,7 @@ class SignInScreen extends StatelessWidget {
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<SignInBloc>(),
|
||||
child: BlocConsumer<SignInBloc, SignInState>(
|
||||
listener: (context, state) {
|
||||
final successOrFail = state.successOrFail;
|
||||
if (successOrFail != null) {
|
||||
handleUserProfileResult(
|
||||
successOrFail,
|
||||
context,
|
||||
getIt<AuthRouter>(),
|
||||
);
|
||||
}
|
||||
},
|
||||
listener: _showSignInError,
|
||||
builder: (context, state) {
|
||||
final isLoading = context.read<SignInBloc>().state.isSubmitting;
|
||||
if (PlatformExtension.isMobile) {
|
||||
@ -43,4 +33,15 @@ class SignInScreen extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showSignInError(BuildContext context, SignInState state) {
|
||||
final successOrFail = state.successOrFail;
|
||||
if (successOrFail != null) {
|
||||
handleUserProfileResult(
|
||||
successOrFail,
|
||||
context,
|
||||
getIt<AuthRouter>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,11 +34,19 @@ class _SignInWithMagicLinkButtonsState
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 48.0,
|
||||
height: PlatformExtension.isMobile ? 38.0 : 48.0,
|
||||
child: FlowyTextField(
|
||||
autoFocus: false,
|
||||
controller: controller,
|
||||
borderRadius: BorderRadius.circular(4.0),
|
||||
hintText: LocaleKeys.signIn_pleaseInputYourEmail.tr(),
|
||||
hintStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 14.0,
|
||||
),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
onSubmitted: (_) => _sendMagicLink(context, controller.text),
|
||||
),
|
||||
@ -88,14 +96,14 @@ class _ConfirmButton extends StatelessWidget {
|
||||
if (PlatformExtension.isMobile) {
|
||||
return ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size(double.infinity, 56),
|
||||
minimumSize: const Size(double.infinity, 32),
|
||||
maximumSize: const Size(double.infinity, 38),
|
||||
),
|
||||
onPressed: onTap,
|
||||
child: FlowyText(
|
||||
name,
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
@ -108,6 +116,7 @@ class _ConfirmButton extends StatelessWidget {
|
||||
text: FlowyText.medium(
|
||||
name,
|
||||
textAlign: TextAlign.center,
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
radius: Corners.s6Border,
|
||||
),
|
||||
|
@ -21,7 +21,11 @@ class SignInAgreement extends StatelessWidget {
|
||||
),
|
||||
TextSpan(
|
||||
text: '${LocaleKeys.web_termOfUse.tr()} ',
|
||||
style: const TextStyle(color: Colors.blue, fontSize: 12),
|
||||
style: const TextStyle(
|
||||
color: Colors.grey,
|
||||
fontSize: 12,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
mouseCursor: SystemMouseCursors.click,
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => afLaunchUrlString('https://appflowy.io/terms'),
|
||||
@ -32,7 +36,11 @@ class SignInAgreement extends StatelessWidget {
|
||||
),
|
||||
TextSpan(
|
||||
text: LocaleKeys.web_privacyPolicy.tr(),
|
||||
style: const TextStyle(color: Colors.blue, fontSize: 12),
|
||||
style: const TextStyle(
|
||||
color: Colors.grey,
|
||||
fontSize: 12,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
mouseCursor: SystemMouseCursors.click,
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => afLaunchUrlString('https://appflowy.io/privacy'),
|
||||
|
@ -89,8 +89,11 @@ class SignInAnonymousButton extends StatelessWidget {
|
||||
class SignInAnonymousButtonV2 extends StatelessWidget {
|
||||
const SignInAnonymousButtonV2({
|
||||
super.key,
|
||||
this.child,
|
||||
});
|
||||
|
||||
final Widget? child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SignInBloc, SignInState>(
|
||||
@ -126,11 +129,12 @@ class SignInAnonymousButtonV2 extends StatelessWidget {
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: GestureDetector(
|
||||
onTap: onTap,
|
||||
child: FlowyText(
|
||||
text,
|
||||
color: Colors.blue,
|
||||
fontSize: 12,
|
||||
),
|
||||
child: child ??
|
||||
FlowyText(
|
||||
text,
|
||||
color: Colors.blue,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -20,7 +20,7 @@ class MobileSignInOrLogoutButton extends StatelessWidget {
|
||||
return GestureDetector(
|
||||
onTap: onPressed,
|
||||
child: Container(
|
||||
height: 48,
|
||||
height: 38,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(4),
|
||||
@ -54,7 +54,7 @@ class MobileSignInOrLogoutButton extends StatelessWidget {
|
||||
FlowyText(
|
||||
labelText,
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -0,0 +1,137 @@
|
||||
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:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum ThirdPartySignInButtonType {
|
||||
apple,
|
||||
google,
|
||||
github,
|
||||
discord,
|
||||
anonymous;
|
||||
|
||||
FlowySvgData get icon {
|
||||
switch (this) {
|
||||
case ThirdPartySignInButtonType.apple:
|
||||
return FlowySvgs.m_apple_icon_xl;
|
||||
case ThirdPartySignInButtonType.google:
|
||||
return FlowySvgs.m_google_icon_xl;
|
||||
case ThirdPartySignInButtonType.github:
|
||||
return FlowySvgs.m_github_icon_xl;
|
||||
case ThirdPartySignInButtonType.discord:
|
||||
return FlowySvgs.m_discord_icon_xl;
|
||||
case ThirdPartySignInButtonType.anonymous:
|
||||
return FlowySvgs.m_discord_icon_xl;
|
||||
}
|
||||
}
|
||||
|
||||
String get labelText {
|
||||
switch (this) {
|
||||
case ThirdPartySignInButtonType.apple:
|
||||
return LocaleKeys.signIn_signInWithApple.tr();
|
||||
case ThirdPartySignInButtonType.google:
|
||||
return LocaleKeys.signIn_signInWithGoogle.tr();
|
||||
case ThirdPartySignInButtonType.github:
|
||||
return LocaleKeys.signIn_signInWithGithub.tr();
|
||||
case ThirdPartySignInButtonType.discord:
|
||||
return LocaleKeys.signIn_signInWithDiscord.tr();
|
||||
case ThirdPartySignInButtonType.anonymous:
|
||||
return 'Anonymous session';
|
||||
}
|
||||
}
|
||||
|
||||
// https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple
|
||||
Color backgroundColor(BuildContext context) {
|
||||
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
||||
switch (this) {
|
||||
case ThirdPartySignInButtonType.apple:
|
||||
return isDarkMode ? Colors.white : Colors.black;
|
||||
case ThirdPartySignInButtonType.google:
|
||||
case ThirdPartySignInButtonType.github:
|
||||
case ThirdPartySignInButtonType.discord:
|
||||
case ThirdPartySignInButtonType.anonymous:
|
||||
return isDarkMode ? Colors.black : Colors.grey.shade100;
|
||||
}
|
||||
}
|
||||
|
||||
Color textColor(BuildContext context) {
|
||||
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
||||
switch (this) {
|
||||
case ThirdPartySignInButtonType.apple:
|
||||
return isDarkMode ? Colors.black : Colors.white;
|
||||
case ThirdPartySignInButtonType.google:
|
||||
case ThirdPartySignInButtonType.github:
|
||||
case ThirdPartySignInButtonType.discord:
|
||||
case ThirdPartySignInButtonType.anonymous:
|
||||
return isDarkMode ? Colors.white : Colors.black;
|
||||
}
|
||||
}
|
||||
|
||||
BlendMode? get blendMode {
|
||||
switch (this) {
|
||||
case ThirdPartySignInButtonType.apple:
|
||||
case ThirdPartySignInButtonType.github:
|
||||
return BlendMode.srcIn;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MobileThirdPartySignInButton extends StatelessWidget {
|
||||
const MobileThirdPartySignInButton({
|
||||
super.key,
|
||||
this.height = 38,
|
||||
this.fontSize = 14.0,
|
||||
required this.onPressed,
|
||||
required this.type,
|
||||
});
|
||||
|
||||
final VoidCallback onPressed;
|
||||
final double height;
|
||||
final double fontSize;
|
||||
final ThirdPartySignInButtonType type;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final style = Theme.of(context);
|
||||
|
||||
return AnimatedGestureDetector(
|
||||
scaleFactor: 1.0,
|
||||
onTapUp: onPressed,
|
||||
child: Container(
|
||||
height: height,
|
||||
decoration: BoxDecoration(
|
||||
color: type.backgroundColor(context),
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(4),
|
||||
),
|
||||
border: Border.all(
|
||||
color: style.colorScheme.outline,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (type != ThirdPartySignInButtonType.anonymous)
|
||||
FlowySvg(
|
||||
type.icon,
|
||||
size: Size.square(fontSize),
|
||||
blendMode: type.blendMode,
|
||||
color: type.textColor(context),
|
||||
),
|
||||
const HSpace(8.0),
|
||||
FlowyText(
|
||||
type.labelText,
|
||||
fontSize: fontSize,
|
||||
color: type.textColor(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,126 +1,167 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/user/application/sign_in_bloc.dart';
|
||||
import 'package:appflowy/user/presentation/presentation.dart';
|
||||
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/sign_in_or_logout_button.dart';
|
||||
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class ThirdPartySignInButtons extends StatelessWidget {
|
||||
import 'third_party_sign_in_button.dart';
|
||||
|
||||
@visibleForTesting
|
||||
const Key signInWithGoogleButtonKey = Key('signInWithGoogleButton');
|
||||
|
||||
class ThirdPartySignInButtons extends StatefulWidget {
|
||||
/// Used in DesktopSignInScreen, MobileSignInScreen and SettingThirdPartyLogin
|
||||
const ThirdPartySignInButtons({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Get themeMode from AppearanceSettingsCubit
|
||||
// When user changes themeMode, it changes the state in AppearanceSettingsCubit, but the themeMode for the MaterialApp won't change, it only got updated(get value from AppearanceSettingsCubit) when user open the app again. Thus, we should get themeMode from AppearanceSettingsCubit rather than MediaQuery.
|
||||
|
||||
final themeModeFromCubit =
|
||||
context.watch<AppearanceSettingsCubit>().state.themeMode;
|
||||
|
||||
final isDarkMode = themeModeFromCubit == ThemeMode.system
|
||||
? MediaQuery.of(context).platformBrightness == Brightness.dark
|
||||
: themeModeFromCubit == ThemeMode.dark;
|
||||
|
||||
return BlocBuilder<SignInBloc, SignInState>(
|
||||
builder: (context, state) {
|
||||
final (googleText, githubText, discordText) = switch (state.loginType) {
|
||||
LoginType.signIn => (
|
||||
LocaleKeys.signIn_signInWithGoogle.tr(),
|
||||
LocaleKeys.signIn_signInWithGithub.tr(),
|
||||
LocaleKeys.signIn_signInWithDiscord.tr()
|
||||
),
|
||||
LoginType.signUp => (
|
||||
LocaleKeys.signIn_signUpWithGoogle.tr(),
|
||||
LocaleKeys.signIn_signUpWithGithub.tr(),
|
||||
LocaleKeys.signIn_signUpWithDiscord.tr()
|
||||
),
|
||||
};
|
||||
return Column(
|
||||
children: [
|
||||
_ThirdPartySignInButton(
|
||||
key: const Key('signInWithGoogleButton'),
|
||||
icon: FlowySvgs.google_mark_xl,
|
||||
labelText: googleText,
|
||||
onPressed: () {
|
||||
_signInWithGoogle(context);
|
||||
},
|
||||
),
|
||||
const VSpace(8),
|
||||
_ThirdPartySignInButton(
|
||||
icon: isDarkMode
|
||||
? FlowySvgs.github_mark_white_xl
|
||||
: FlowySvgs.github_mark_black_xl,
|
||||
labelText: githubText,
|
||||
onPressed: () {
|
||||
_signInWithGithub(context);
|
||||
},
|
||||
),
|
||||
const VSpace(8),
|
||||
_ThirdPartySignInButton(
|
||||
icon: isDarkMode
|
||||
? FlowySvgs.discord_mark_white_xl
|
||||
: FlowySvgs.discord_mark_blurple_xl,
|
||||
labelText: discordText,
|
||||
onPressed: () {
|
||||
_signInWithDiscord(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ThirdPartySignInButton extends StatelessWidget {
|
||||
/// Build button based on current Platform(mobile or desktop).
|
||||
const _ThirdPartySignInButton({
|
||||
const ThirdPartySignInButtons({
|
||||
super.key,
|
||||
required this.icon,
|
||||
required this.labelText,
|
||||
required this.onPressed,
|
||||
this.expanded = false,
|
||||
});
|
||||
|
||||
final FlowySvgData icon;
|
||||
final String labelText;
|
||||
final bool expanded;
|
||||
|
||||
final VoidCallback onPressed;
|
||||
@override
|
||||
State<ThirdPartySignInButtons> createState() =>
|
||||
_ThirdPartySignInButtonsState();
|
||||
}
|
||||
|
||||
class _ThirdPartySignInButtonsState extends State<ThirdPartySignInButtons> {
|
||||
bool expanded = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
expanded = widget.expanded;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (PlatformExtension.isMobile) {
|
||||
return MobileSignInOrLogoutButton(
|
||||
icon: icon,
|
||||
labelText: labelText,
|
||||
onPressed: onPressed,
|
||||
if (PlatformExtension.isDesktopOrWeb) {
|
||||
const padding = 16.0;
|
||||
return Column(
|
||||
children: [
|
||||
_DesktopSignInButton(
|
||||
key: signInWithGoogleButtonKey,
|
||||
type: ThirdPartySignInButtonType.google,
|
||||
onPressed: () {
|
||||
_signInWithGoogle(context);
|
||||
},
|
||||
),
|
||||
const VSpace(padding),
|
||||
_DesktopSignInButton(
|
||||
type: ThirdPartySignInButtonType.github,
|
||||
onPressed: () {
|
||||
_signInWithGithub(context);
|
||||
},
|
||||
),
|
||||
const VSpace(padding),
|
||||
_DesktopSignInButton(
|
||||
type: ThirdPartySignInButtonType.discord,
|
||||
onPressed: () {
|
||||
_signInWithDiscord(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return _DesktopSignInButton(
|
||||
icon: icon,
|
||||
labelText: labelText,
|
||||
onPressed: onPressed,
|
||||
const padding = 8.0;
|
||||
return BlocBuilder<SignInBloc, SignInState>(
|
||||
builder: (context, state) {
|
||||
return Column(
|
||||
children: [
|
||||
if (Platform.isIOS) ...[
|
||||
MobileThirdPartySignInButton(
|
||||
type: ThirdPartySignInButtonType.apple,
|
||||
onPressed: () {
|
||||
_signInWithApple(context);
|
||||
},
|
||||
),
|
||||
const VSpace(padding),
|
||||
],
|
||||
MobileThirdPartySignInButton(
|
||||
type: ThirdPartySignInButtonType.google,
|
||||
onPressed: () {
|
||||
_signInWithGoogle(context);
|
||||
},
|
||||
),
|
||||
if (expanded) ...[
|
||||
const VSpace(padding),
|
||||
MobileThirdPartySignInButton(
|
||||
type: ThirdPartySignInButtonType.github,
|
||||
onPressed: () {
|
||||
_signInWithGithub(context);
|
||||
},
|
||||
),
|
||||
const VSpace(padding),
|
||||
MobileThirdPartySignInButton(
|
||||
type: ThirdPartySignInButtonType.discord,
|
||||
onPressed: () {
|
||||
_signInWithDiscord(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
if (!expanded) ...[
|
||||
const VSpace(padding * 2),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
expanded = !expanded;
|
||||
});
|
||||
},
|
||||
child: FlowyText(
|
||||
LocaleKeys.signIn_continueAnotherWay.tr(),
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
decoration: TextDecoration.underline,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _signInWithApple(BuildContext context) {
|
||||
context.read<SignInBloc>().add(
|
||||
const SignInEvent.signedInWithOAuth('apple'),
|
||||
);
|
||||
}
|
||||
|
||||
void _signInWithGoogle(BuildContext context) {
|
||||
context.read<SignInBloc>().add(
|
||||
const SignInEvent.signedInWithOAuth('google'),
|
||||
);
|
||||
}
|
||||
|
||||
void _signInWithGithub(BuildContext context) {
|
||||
context
|
||||
.read<SignInBloc>()
|
||||
.add(const SignInEvent.signedInWithOAuth('github'));
|
||||
}
|
||||
|
||||
void _signInWithDiscord(BuildContext context) {
|
||||
context
|
||||
.read<SignInBloc>()
|
||||
.add(const SignInEvent.signedInWithOAuth('discord'));
|
||||
}
|
||||
}
|
||||
|
||||
class _DesktopSignInButton extends StatelessWidget {
|
||||
const _DesktopSignInButton({
|
||||
required this.icon,
|
||||
required this.labelText,
|
||||
super.key,
|
||||
required this.type,
|
||||
required this.onPressed,
|
||||
});
|
||||
|
||||
final FlowySvgData icon;
|
||||
final String labelText;
|
||||
|
||||
final ThirdPartySignInButtonType type;
|
||||
final VoidCallback onPressed;
|
||||
|
||||
@override
|
||||
@ -139,8 +180,8 @@ class _DesktopSignInButton extends StatelessWidget {
|
||||
// Some icons are not square, so we just use a fixed width here.
|
||||
width: 24,
|
||||
child: FlowySvg(
|
||||
icon,
|
||||
blendMode: null,
|
||||
type.icon,
|
||||
blendMode: type.blendMode,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -148,7 +189,7 @@ class _DesktopSignInButton extends StatelessWidget {
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: FlowyText(
|
||||
labelText,
|
||||
type.labelText,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
@ -177,19 +218,3 @@ class _DesktopSignInButton extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _signInWithGoogle(BuildContext context) {
|
||||
context.read<SignInBloc>().add(
|
||||
const SignInEvent.signedInWithOAuth('google'),
|
||||
);
|
||||
}
|
||||
|
||||
void _signInWithGithub(BuildContext context) {
|
||||
context.read<SignInBloc>().add(const SignInEvent.signedInWithOAuth('github'));
|
||||
}
|
||||
|
||||
void _signInWithDiscord(BuildContext context) {
|
||||
context
|
||||
.read<SignInBloc>()
|
||||
.add(const SignInEvent.signedInWithOAuth('discord'));
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
export 'magic_link_sign_in_buttons.dart';
|
||||
export 'sign_in_anonymous_button.dart';
|
||||
export 'sign_in_or_logout_button.dart';
|
||||
|
||||
export 'third_party_sign_in_button.dart';
|
||||
// export 'switch_sign_in_sign_up_button.dart';
|
||||
export 'third_party_sign_in_buttons.dart';
|
||||
export 'sign_in_agreement.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();
|
||||
}
|
||||
}
|
||||
|
@ -296,7 +296,7 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
|
||||
),
|
||||
);
|
||||
},
|
||||
reset: (userProfile, workspaceId) async {
|
||||
reset: (userProfile, workspaceId, openFirstPage) async {
|
||||
if (workspaceId == _workspaceId) {
|
||||
return;
|
||||
}
|
||||
@ -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());
|
||||
},
|
||||
);
|
||||
@ -559,7 +562,10 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
|
||||
return true;
|
||||
}
|
||||
|
||||
final viewId = fixedUuid(user.id.toInt(), UuidType.publicSpace);
|
||||
final viewId = fixedUuid(
|
||||
user.id.toInt() + (_workspaceId?.hashCode ?? 0),
|
||||
UuidType.publicSpace,
|
||||
);
|
||||
final publicSpace = await _createSpace(
|
||||
name: 'Shared',
|
||||
icon: builtInSpaceIcons.first,
|
||||
@ -732,6 +738,7 @@ class SpaceEvent with _$SpaceEvent {
|
||||
const factory SpaceEvent.reset(
|
||||
UserProfilePB userProfile,
|
||||
String workspaceId,
|
||||
bool openFirstPage,
|
||||
) = _Reset;
|
||||
const factory SpaceEvent.migrate() = _Migrate;
|
||||
const factory SpaceEvent.switchToNextSpace() = _SwitchToNextSpace;
|
||||
|
@ -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:
|
||||
|
@ -196,6 +196,7 @@ class HomeSideBar extends StatelessWidget {
|
||||
userProfile,
|
||||
state.currentWorkspace?.workspaceId ??
|
||||
workspaceSetting.workspaceId,
|
||||
true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/user/user_workspace_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/hotkeys.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/favorites/favorite_folder.dart';
|
||||
@ -31,7 +32,6 @@ class SidebarSpace extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// const sectionPadding = 16.0;
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: getIt<MenuSharedState>().notifier,
|
||||
builder: (context, value, child) {
|
||||
@ -89,6 +89,8 @@ class _SpaceState extends State<_Space> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final currentWorkspace =
|
||||
context.watch<UserWorkspaceBloc>().state.currentWorkspace;
|
||||
return BlocBuilder<SpaceBloc, SpaceState>(
|
||||
builder: (context, state) {
|
||||
if (state.spaces.isEmpty) {
|
||||
@ -115,7 +117,12 @@ class _SpaceState extends State<_Space> {
|
||||
onEnter: (_) => isHovered.value = true,
|
||||
onExit: (_) => isHovered.value = false,
|
||||
child: SpacePages(
|
||||
key: ValueKey(currentSpace.id),
|
||||
key: ValueKey(
|
||||
Object.hashAll([
|
||||
currentWorkspace?.workspaceId ?? '',
|
||||
currentSpace.id,
|
||||
]),
|
||||
),
|
||||
isExpandedNotifier: isExpandedNotifier,
|
||||
space: currentSpace,
|
||||
isHovered: isHovered,
|
||||
|
@ -107,7 +107,6 @@ class SingleSettingAction extends StatelessWidget {
|
||||
radius: Corners.s8Border,
|
||||
hoverColor: hoverColor(context),
|
||||
fontColor: fontColor(context),
|
||||
textColor: fontColor(context),
|
||||
fontHoverColor: fontHoverColor(context),
|
||||
borderColor: borderColor(context),
|
||||
fontSize: 12,
|
||||
|
@ -447,7 +447,7 @@ class _IncludeTimePickerState extends State<_IncludeTimePicker> {
|
||||
LocaleKeys.button_confirm.tr(),
|
||||
constraints: const BoxConstraints.tightFor(height: 42),
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
fontColor: Theme.of(context).colorScheme.onPrimary,
|
||||
fillColor: Theme.of(context).primaryColor,
|
||||
onPressed: () {
|
||||
if (isStartDay) {
|
||||
|
@ -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();
|
||||
},
|
||||
@ -303,14 +310,18 @@ void showToastNotification(
|
||||
required String message,
|
||||
String? description,
|
||||
ToastificationType type = ToastificationType.success,
|
||||
ToastificationCallbacks? callbacks,
|
||||
double bottomPadding = 100,
|
||||
}) {
|
||||
if (PlatformExtension.isMobile) {
|
||||
toastification.showCustom(
|
||||
alignment: Alignment.bottomCenter,
|
||||
autoCloseDuration: const Duration(milliseconds: 3000),
|
||||
callbacks: callbacks ?? const ToastificationCallbacks(),
|
||||
builder: (_, __) => _MToast(
|
||||
message: message,
|
||||
type: type,
|
||||
bottomPadding: bottomPadding,
|
||||
),
|
||||
);
|
||||
return;
|
||||
@ -346,10 +357,12 @@ class _MToast extends StatelessWidget {
|
||||
const _MToast({
|
||||
required this.message,
|
||||
this.type = ToastificationType.success,
|
||||
this.bottomPadding = 100,
|
||||
});
|
||||
|
||||
final String message;
|
||||
final ToastificationType type;
|
||||
final double bottomPadding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -362,7 +375,7 @@ class _MToast extends StatelessWidget {
|
||||
);
|
||||
return Container(
|
||||
alignment: Alignment.bottomCenter,
|
||||
padding: const EdgeInsets.only(bottom: 100, left: 16, right: 16),
|
||||
padding: EdgeInsets.only(bottom: bottomPadding, left: 16, right: 16),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 13.0),
|
||||
decoration: BoxDecoration(
|
||||
|
@ -297,7 +297,6 @@ class FlowyTextButton extends StatelessWidget {
|
||||
this.padding = const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
||||
this.hoverColor,
|
||||
this.fillColor,
|
||||
this.textColor,
|
||||
this.heading,
|
||||
this.radius,
|
||||
this.mainAxisAlignment = MainAxisAlignment.start,
|
||||
@ -353,7 +352,6 @@ class FlowyTextButton extends StatelessWidget {
|
||||
final Widget? heading;
|
||||
final Color? hoverColor;
|
||||
final Color? fillColor;
|
||||
final Color? textColor;
|
||||
final BorderRadius? radius;
|
||||
final MainAxisAlignment mainAxisAlignment;
|
||||
final String? tooltip;
|
||||
@ -376,9 +374,10 @@ class FlowyTextButton extends StatelessWidget {
|
||||
children.add(FlowyText(
|
||||
text,
|
||||
overflow: overflow,
|
||||
color: textColor,
|
||||
color: fontColor ?? Theme.of(context).colorScheme.onPrimary,
|
||||
textAlign: TextAlign.center,
|
||||
lineHeight: lineHeight,
|
||||
fontSize: fontSize,
|
||||
));
|
||||
|
||||
Widget child = Row(
|
||||
|
@ -38,6 +38,7 @@ class FlowyTextField extends StatefulWidget {
|
||||
final bool isDense;
|
||||
final bool readOnly;
|
||||
final Color? enableBorderColor;
|
||||
final BorderRadius? borderRadius;
|
||||
|
||||
const FlowyTextField({
|
||||
super.key,
|
||||
@ -74,6 +75,7 @@ class FlowyTextField extends StatefulWidget {
|
||||
this.isDense = true,
|
||||
this.readOnly = false,
|
||||
this.enableBorderColor,
|
||||
this.borderRadius,
|
||||
});
|
||||
|
||||
@override
|
||||
@ -180,7 +182,7 @@ class FlowyTextFieldState extends State<FlowyTextField> {
|
||||
(widget.maxLines == null || widget.maxLines! > 1) ? 12 : 0,
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: Corners.s8Border,
|
||||
borderRadius: widget.borderRadius ?? Corners.s8Border,
|
||||
borderSide: BorderSide(
|
||||
color: widget.enableBorderColor ??
|
||||
Theme.of(context).colorScheme.outline,
|
||||
@ -202,7 +204,7 @@ class FlowyTextFieldState extends State<FlowyTextField> {
|
||||
suffixText: widget.showCounter ? _suffixText() : "",
|
||||
counterText: "",
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: Corners.s8Border,
|
||||
borderRadius: widget.borderRadius ?? Corners.s8Border,
|
||||
borderSide: BorderSide(
|
||||
color: widget.readOnly
|
||||
? widget.enableBorderColor ??
|
||||
@ -214,13 +216,13 @@ class FlowyTextFieldState extends State<FlowyTextField> {
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
borderRadius: Corners.s8Border,
|
||||
borderRadius: widget.borderRadius ?? Corners.s8Border,
|
||||
),
|
||||
focusedErrorBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
borderRadius: Corners.s8Border,
|
||||
borderRadius: widget.borderRadius ?? Corners.s8Border,
|
||||
),
|
||||
prefixIcon: widget.prefixIcon,
|
||||
suffixIcon: widget.suffixIcon,
|
||||
|
@ -51,11 +51,9 @@ class RoundedTextButton extends StatelessWidget {
|
||||
radius: borderRadius ?? Corners.s6Border,
|
||||
fontColor: textColor ?? Theme.of(context).colorScheme.onPrimary,
|
||||
fillColor: fillColor ?? Theme.of(context).colorScheme.primary,
|
||||
textColor: textColor,
|
||||
hoverColor:
|
||||
hoverColor ?? Theme.of(context).colorScheme.primaryContainer,
|
||||
padding: padding,
|
||||
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -53,8 +53,8 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: "8e17d14"
|
||||
resolved-ref: "8e17d1447eea0b57ff92e31dbe88796ce759fb37"
|
||||
ref: a64a516
|
||||
resolved-ref: a64a5165e79bd2424e594b793843a7158e7d4fb4
|
||||
url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
|
||||
source: git
|
||||
version: "3.2.0"
|
||||
|
@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 0.6.7
|
||||
version: 0.6.8
|
||||
|
||||
environment:
|
||||
flutter: ">=3.22.0"
|
||||
@ -190,7 +190,7 @@ dependency_overrides:
|
||||
appflowy_editor:
|
||||
git:
|
||||
url: https://github.com/AppFlowy-IO/appflowy-editor.git
|
||||
ref: "8e17d14"
|
||||
ref: "a64a516"
|
||||
|
||||
appflowy_editor_plugins:
|
||||
git:
|
||||
|
94
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=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
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=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -291,6 +291,17 @@ version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
||||
|
||||
[[package]]
|
||||
name = "async-lock"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
|
||||
dependencies = [
|
||||
"event-listener",
|
||||
"event-listener-strategy",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stream"
|
||||
version = "0.3.5"
|
||||
@ -826,7 +837,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"again",
|
||||
"anyhow",
|
||||
@ -877,7 +888,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"collab-entity",
|
||||
"collab-rt-entity",
|
||||
@ -890,7 +901,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-websocket"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
@ -964,7 +975,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=520b75dbe84ff17a55cf9424a55028781ae0bfc6#520b75dbe84ff17a55cf9424a55028781ae0bfc6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=49cf2206d7494bb3006402b807e7f171905213e3#49cf2206d7494bb3006402b807e7f171905213e3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@ -989,14 +1000,13 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-database"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=520b75dbe84ff17a55cf9424a55028781ae0bfc6#520b75dbe84ff17a55cf9424a55028781ae0bfc6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=49cf2206d7494bb3006402b807e7f171905213e3#49cf2206d7494bb3006402b807e7f171905213e3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"chrono",
|
||||
"collab",
|
||||
"collab-entity",
|
||||
"collab-plugins",
|
||||
"dashmap 5.5.3",
|
||||
"futures",
|
||||
"getrandom 0.2.10",
|
||||
@ -1019,7 +1029,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-document"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=520b75dbe84ff17a55cf9424a55028781ae0bfc6#520b75dbe84ff17a55cf9424a55028781ae0bfc6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=49cf2206d7494bb3006402b807e7f171905213e3#49cf2206d7494bb3006402b807e7f171905213e3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@ -1039,7 +1049,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-entity"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=520b75dbe84ff17a55cf9424a55028781ae0bfc6#520b75dbe84ff17a55cf9424a55028781ae0bfc6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=49cf2206d7494bb3006402b807e7f171905213e3#49cf2206d7494bb3006402b807e7f171905213e3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -1058,7 +1068,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-folder"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=520b75dbe84ff17a55cf9424a55028781ae0bfc6#520b75dbe84ff17a55cf9424a55028781ae0bfc6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=49cf2206d7494bb3006402b807e7f171905213e3#49cf2206d7494bb3006402b807e7f171905213e3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@ -1101,7 +1111,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-plugins"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=520b75dbe84ff17a55cf9424a55028781ae0bfc6#520b75dbe84ff17a55cf9424a55028781ae0bfc6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=49cf2206d7494bb3006402b807e7f171905213e3#49cf2206d7494bb3006402b807e7f171905213e3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@ -1139,7 +1149,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-rt-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -1164,7 +1174,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-rt-protocol"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -1181,7 +1191,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-user"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=520b75dbe84ff17a55cf9424a55028781ae0bfc6#520b75dbe84ff17a55cf9424a55028781ae0bfc6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=49cf2206d7494bb3006402b807e7f171905213e3#49cf2206d7494bb3006402b807e7f171905213e3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
@ -1210,6 +1220,15 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.14.1"
|
||||
@ -1552,7 +1571,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
||||
[[package]]
|
||||
name = "database-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -1865,6 +1884,27 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "5.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"parking",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener-strategy"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1"
|
||||
dependencies = [
|
||||
"event-listener",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "faccess"
|
||||
version = "0.2.4"
|
||||
@ -3077,7 +3117,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
@ -3094,7 +3134,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -3526,7 +3566,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "infra"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -4616,6 +4656,12 @@ dependencies = [
|
||||
"system-deps 6.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.2"
|
||||
@ -6123,7 +6169,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "shared-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -8401,12 +8447,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "yrs"
|
||||
version = "0.19.2"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8ca5126331b9a5ef5bb10f3f1c3d01b05f298d348c66f8fb15497d83ee73176"
|
||||
checksum = "a8fc56b25e3aaf4b81a73f2a9a68ceae1e02d9005552e24058cfb9f96db73f33"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"atomic_refcell",
|
||||
"async-lock",
|
||||
"async-trait",
|
||||
"dashmap 6.0.1",
|
||||
"fastrand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -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 = "ae3833ea91c238a66ca7bda63763d1d654740fb4" }
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "c9504d4081a8d9893830dc6598429353b078271b" }
|
||||
|
||||
[dependencies]
|
||||
serde_json.workspace = true
|
||||
@ -116,13 +116,13 @@ custom-protocol = ["tauri/custom-protocol"]
|
||||
# To switch to the local path, run:
|
||||
# scripts/tool/update_collab_source.sh
|
||||
# ⚠️⚠️⚠️️
|
||||
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "520b75dbe84ff17a55cf9424a55028781ae0bfc6" }
|
||||
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "520b75dbe84ff17a55cf9424a55028781ae0bfc6" }
|
||||
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "520b75dbe84ff17a55cf9424a55028781ae0bfc6" }
|
||||
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "520b75dbe84ff17a55cf9424a55028781ae0bfc6" }
|
||||
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "520b75dbe84ff17a55cf9424a55028781ae0bfc6" }
|
||||
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "520b75dbe84ff17a55cf9424a55028781ae0bfc6" }
|
||||
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "520b75dbe84ff17a55cf9424a55028781ae0bfc6" }
|
||||
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "49cf2206d7494bb3006402b807e7f171905213e3" }
|
||||
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "49cf2206d7494bb3006402b807e7f171905213e3" }
|
||||
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "49cf2206d7494bb3006402b807e7f171905213e3" }
|
||||
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "49cf2206d7494bb3006402b807e7f171905213e3" }
|
||||
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "49cf2206d7494bb3006402b807e7f171905213e3" }
|
||||
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "49cf2206d7494bb3006402b807e7f171905213e3" }
|
||||
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "49cf2206d7494bb3006402b807e7f171905213e3" }
|
||||
|
||||
# Working directory: frontend
|
||||
# To update the commit ID, run:
|
||||
|
@ -68,7 +68,7 @@ const createServer = async (req: Request) => {
|
||||
|
||||
logger.info(`Request URL: ${hostname}${reqUrl.pathname}`);
|
||||
|
||||
if (['/after-payment', '/login'].includes(reqUrl.pathname)) {
|
||||
if (['/after-payment', '/login', '/as-template'].includes(reqUrl.pathname)) {
|
||||
timer();
|
||||
const htmlData = fs.readFileSync(indexPath, 'utf8');
|
||||
const $ = load(htmlData);
|
||||
|
@ -73,6 +73,7 @@
|
||||
"react-datepicker": "^4.23.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-error-boundary": "^4.0.13",
|
||||
"react-hook-form": "^7.52.2",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"react-i18next": "^14.1.0",
|
||||
"react-katex": "^3.0.1",
|
||||
@ -137,6 +138,7 @@
|
||||
"@typescript-eslint/parser": "^7.2.0",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"axios-mock-adapter": "^2.0.0",
|
||||
"babel-jest": "^29.6.2",
|
||||
"chalk": "^4.1.2",
|
||||
"cheerio": "1.0.0-rc.12",
|
||||
|
33
frontend/appflowy_web_app/pnpm-lock.yaml
generated
@ -152,6 +152,9 @@ dependencies:
|
||||
react-error-boundary:
|
||||
specifier: ^4.0.13
|
||||
version: 4.0.13(react@18.2.0)
|
||||
react-hook-form:
|
||||
specifier: ^7.52.2
|
||||
version: 7.52.2(react@18.2.0)
|
||||
react-hot-toast:
|
||||
specifier: ^2.4.1
|
||||
version: 2.4.1(csstype@3.1.3)(react-dom@18.2.0)(react@18.2.0)
|
||||
@ -340,6 +343,9 @@ devDependencies:
|
||||
autoprefixer:
|
||||
specifier: ^10.4.13
|
||||
version: 10.4.13(postcss@8.4.21)
|
||||
axios-mock-adapter:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0(axios@1.7.2)
|
||||
babel-jest:
|
||||
specifier: ^29.6.2
|
||||
version: 29.6.2(@babel/core@7.24.3)
|
||||
@ -4945,6 +4951,16 @@ packages:
|
||||
/aws4@1.12.0:
|
||||
resolution: {integrity: sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==}
|
||||
|
||||
/axios-mock-adapter@2.0.0(axios@1.7.2):
|
||||
resolution: {integrity: sha512-D/K0J5Zm6KvaMTnsWrBQZWLzKN9GxUFZEa0mx2qeEHXDeTugCoplWehy8y36dj5vuSjhe1u/Dol8cZ8lzzmDew==}
|
||||
peerDependencies:
|
||||
axios: '>= 0.17.0'
|
||||
dependencies:
|
||||
axios: 1.7.2
|
||||
fast-deep-equal: 3.1.3
|
||||
is-buffer: 2.0.5
|
||||
dev: true
|
||||
|
||||
/axios@1.7.2:
|
||||
resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==}
|
||||
dependencies:
|
||||
@ -4953,7 +4969,6 @@ packages:
|
||||
proxy-from-env: 1.1.0
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
dev: false
|
||||
|
||||
/b4a@1.6.6:
|
||||
resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==}
|
||||
@ -6709,7 +6724,6 @@ packages:
|
||||
peerDependenciesMeta:
|
||||
debug:
|
||||
optional: true
|
||||
dev: false
|
||||
|
||||
/for-each@0.3.3:
|
||||
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
|
||||
@ -7289,6 +7303,11 @@ packages:
|
||||
resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==}
|
||||
dev: false
|
||||
|
||||
/is-buffer@2.0.5:
|
||||
resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==}
|
||||
engines: {node: '>=4'}
|
||||
dev: true
|
||||
|
||||
/is-callable@1.2.7:
|
||||
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -9258,7 +9277,6 @@ packages:
|
||||
|
||||
/proxy-from-env@1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
dev: false
|
||||
|
||||
/psl@1.9.0:
|
||||
resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
|
||||
@ -9512,6 +9530,15 @@ packages:
|
||||
/react-fast-compare@3.2.2:
|
||||
resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==}
|
||||
|
||||
/react-hook-form@7.52.2(react@18.2.0):
|
||||
resolution: {integrity: sha512-pqfPEbERnxxiNMPd0bzmt1tuaPcVccywFDpyk2uV5xCIBphHV5T8SVnX9/o3kplPE1zzKt77+YIoq+EMwJp56A==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17 || ^18 || ^19
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/react-hot-toast@2.4.1(csstype@3.1.3)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
94
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=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
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=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -301,6 +301,17 @@ version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
||||
|
||||
[[package]]
|
||||
name = "async-lock"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
|
||||
dependencies = [
|
||||
"event-listener",
|
||||
"event-listener-strategy",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stream"
|
||||
version = "0.3.5"
|
||||
@ -800,7 +811,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"again",
|
||||
"anyhow",
|
||||
@ -851,7 +862,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"collab-entity",
|
||||
"collab-rt-entity",
|
||||
@ -864,7 +875,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-websocket"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
@ -947,7 +958,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=520b75dbe84ff17a55cf9424a55028781ae0bfc6#520b75dbe84ff17a55cf9424a55028781ae0bfc6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=49cf2206d7494bb3006402b807e7f171905213e3#49cf2206d7494bb3006402b807e7f171905213e3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@ -972,14 +983,13 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-database"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=520b75dbe84ff17a55cf9424a55028781ae0bfc6#520b75dbe84ff17a55cf9424a55028781ae0bfc6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=49cf2206d7494bb3006402b807e7f171905213e3#49cf2206d7494bb3006402b807e7f171905213e3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"chrono",
|
||||
"collab",
|
||||
"collab-entity",
|
||||
"collab-plugins",
|
||||
"dashmap 5.5.3",
|
||||
"futures",
|
||||
"getrandom 0.2.12",
|
||||
@ -1002,7 +1012,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-document"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=520b75dbe84ff17a55cf9424a55028781ae0bfc6#520b75dbe84ff17a55cf9424a55028781ae0bfc6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=49cf2206d7494bb3006402b807e7f171905213e3#49cf2206d7494bb3006402b807e7f171905213e3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@ -1022,7 +1032,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-entity"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=520b75dbe84ff17a55cf9424a55028781ae0bfc6#520b75dbe84ff17a55cf9424a55028781ae0bfc6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=49cf2206d7494bb3006402b807e7f171905213e3#49cf2206d7494bb3006402b807e7f171905213e3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -1041,7 +1051,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-folder"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=520b75dbe84ff17a55cf9424a55028781ae0bfc6#520b75dbe84ff17a55cf9424a55028781ae0bfc6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=49cf2206d7494bb3006402b807e7f171905213e3#49cf2206d7494bb3006402b807e7f171905213e3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@ -1084,7 +1094,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-plugins"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=520b75dbe84ff17a55cf9424a55028781ae0bfc6#520b75dbe84ff17a55cf9424a55028781ae0bfc6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=49cf2206d7494bb3006402b807e7f171905213e3#49cf2206d7494bb3006402b807e7f171905213e3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@ -1122,7 +1132,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-rt-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -1147,7 +1157,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-rt-protocol"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -1164,7 +1174,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-user"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=520b75dbe84ff17a55cf9424a55028781ae0bfc6#520b75dbe84ff17a55cf9424a55028781ae0bfc6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=49cf2206d7494bb3006402b807e7f171905213e3#49cf2206d7494bb3006402b807e7f171905213e3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
@ -1193,6 +1203,15 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.14.1"
|
||||
@ -1542,7 +1561,7 @@ checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
|
||||
[[package]]
|
||||
name = "database-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -1895,6 +1914,27 @@ version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b"
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "5.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"parking",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener-strategy"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1"
|
||||
dependencies = [
|
||||
"event-listener",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "faccess"
|
||||
version = "0.2.4"
|
||||
@ -3144,7 +3184,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
@ -3161,7 +3201,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -3598,7 +3638,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "infra"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -4678,6 +4718,12 @@ dependencies = [
|
||||
"system-deps 6.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.2"
|
||||
@ -6187,7 +6233,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "shared-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ae3833ea91c238a66ca7bda63763d1d654740fb4#ae3833ea91c238a66ca7bda63763d1d654740fb4"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c9504d4081a8d9893830dc6598429353b078271b#c9504d4081a8d9893830dc6598429353b078271b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -8659,12 +8705,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "yrs"
|
||||
version = "0.19.2"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8ca5126331b9a5ef5bb10f3f1c3d01b05f298d348c66f8fb15497d83ee73176"
|
||||
checksum = "a8fc56b25e3aaf4b81a73f2a9a68ceae1e02d9005552e24058cfb9f96db73f33"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"atomic_refcell",
|
||||
"async-lock",
|
||||
"async-trait",
|
||||
"dashmap 6.0.1",
|
||||
"fastrand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -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 = "ae3833ea91c238a66ca7bda63763d1d654740fb4" }
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "c9504d4081a8d9893830dc6598429353b078271b" }
|
||||
|
||||
[dependencies]
|
||||
serde_json.workspace = true
|
||||
@ -116,13 +116,13 @@ custom-protocol = ["tauri/custom-protocol"]
|
||||
# To switch to the local path, run:
|
||||
# scripts/tool/update_collab_source.sh
|
||||
# ⚠️⚠️⚠️️
|
||||
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "520b75dbe84ff17a55cf9424a55028781ae0bfc6" }
|
||||
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "520b75dbe84ff17a55cf9424a55028781ae0bfc6" }
|
||||
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "520b75dbe84ff17a55cf9424a55028781ae0bfc6" }
|
||||
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "520b75dbe84ff17a55cf9424a55028781ae0bfc6" }
|
||||
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "520b75dbe84ff17a55cf9424a55028781ae0bfc6" }
|
||||
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "520b75dbe84ff17a55cf9424a55028781ae0bfc6" }
|
||||
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "520b75dbe84ff17a55cf9424a55028781ae0bfc6" }
|
||||
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "49cf2206d7494bb3006402b807e7f171905213e3" }
|
||||
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "49cf2206d7494bb3006402b807e7f171905213e3" }
|
||||
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "49cf2206d7494bb3006402b807e7f171905213e3" }
|
||||
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "49cf2206d7494bb3006402b807e7f171905213e3" }
|
||||
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "49cf2206d7494bb3006402b807e7f171905213e3" }
|
||||
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "49cf2206d7494bb3006402b807e7f171905213e3" }
|
||||
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "49cf2206d7494bb3006402b807e7f171905213e3" }
|
||||
|
||||
# Working directory: frontend
|
||||
# To update the commit ID, run:
|
||||
|
@ -34,6 +34,7 @@ export enum BlockType {
|
||||
TableBlock = 'table',
|
||||
TableCell = 'table/cell',
|
||||
LinkPreview = 'link_preview',
|
||||
FileBlock = 'file',
|
||||
}
|
||||
|
||||
export enum InlineBlockType {
|
||||
@ -85,6 +86,18 @@ export interface LinkPreviewBlockData extends BlockData {
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export enum FieldURLType {
|
||||
Upload = 2,
|
||||
Link = 1,
|
||||
}
|
||||
|
||||
export interface FileBlockData extends BlockData {
|
||||
name: string;
|
||||
uploaded_at: number;
|
||||
url: string;
|
||||
url_type: FieldURLType;
|
||||
}
|
||||
|
||||
export enum ImageType {
|
||||
Local = 0,
|
||||
Internal = 1,
|
||||
@ -271,151 +284,151 @@ export enum YjsDatabaseKey {
|
||||
|
||||
export interface YDoc extends Y.Doc {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
getMap(key: YjsEditorKey.data_section): YSharedRoot | any;
|
||||
getMap (key: YjsEditorKey.data_section): YSharedRoot | any;
|
||||
}
|
||||
|
||||
export interface YDatabaseRow extends Y.Map<unknown> {
|
||||
get(key: YjsDatabaseKey.id): RowId;
|
||||
get (key: YjsDatabaseKey.id): RowId;
|
||||
|
||||
get(key: YjsDatabaseKey.height): string;
|
||||
get (key: YjsDatabaseKey.height): string;
|
||||
|
||||
get(key: YjsDatabaseKey.visibility): boolean;
|
||||
get (key: YjsDatabaseKey.visibility): boolean;
|
||||
|
||||
get(key: YjsDatabaseKey.created_at): CreatedAt;
|
||||
get (key: YjsDatabaseKey.created_at): CreatedAt;
|
||||
|
||||
get(key: YjsDatabaseKey.last_modified): LastModified;
|
||||
get (key: YjsDatabaseKey.last_modified): LastModified;
|
||||
|
||||
get(key: YjsDatabaseKey.cells): YDatabaseCells;
|
||||
get (key: YjsDatabaseKey.cells): YDatabaseCells;
|
||||
}
|
||||
|
||||
export interface YDatabaseCells extends Y.Map<unknown> {
|
||||
get(key: FieldId): YDatabaseCell;
|
||||
get (key: FieldId): YDatabaseCell;
|
||||
}
|
||||
|
||||
export type EndTimestamp = string;
|
||||
export type ReminderId = string;
|
||||
|
||||
export interface YDatabaseCell extends Y.Map<unknown> {
|
||||
get(key: YjsDatabaseKey.created_at): CreatedAt;
|
||||
get (key: YjsDatabaseKey.created_at): CreatedAt;
|
||||
|
||||
get(key: YjsDatabaseKey.last_modified): LastModified;
|
||||
get (key: YjsDatabaseKey.last_modified): LastModified;
|
||||
|
||||
get(key: YjsDatabaseKey.field_type): string;
|
||||
get (key: YjsDatabaseKey.field_type): string;
|
||||
|
||||
get(key: YjsDatabaseKey.data): object | string | boolean | number;
|
||||
get (key: YjsDatabaseKey.data): object | string | boolean | number;
|
||||
|
||||
get(key: YjsDatabaseKey.end_timestamp): EndTimestamp;
|
||||
get (key: YjsDatabaseKey.end_timestamp): EndTimestamp;
|
||||
|
||||
get(key: YjsDatabaseKey.include_time): boolean;
|
||||
get (key: YjsDatabaseKey.include_time): boolean;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/unified-signatures
|
||||
get(key: YjsDatabaseKey.is_range): boolean;
|
||||
get (key: YjsDatabaseKey.is_range): boolean;
|
||||
|
||||
get(key: YjsDatabaseKey.reminder_id): ReminderId;
|
||||
get (key: YjsDatabaseKey.reminder_id): ReminderId;
|
||||
}
|
||||
|
||||
export interface YSharedRoot extends Y.Map<unknown> {
|
||||
get(key: YjsEditorKey.document): YDocument;
|
||||
get (key: YjsEditorKey.document): YDocument;
|
||||
|
||||
get(key: YjsEditorKey.folder): YFolder;
|
||||
get (key: YjsEditorKey.folder): YFolder;
|
||||
|
||||
get(key: YjsEditorKey.database): YDatabase;
|
||||
get (key: YjsEditorKey.database): YDatabase;
|
||||
|
||||
get(key: YjsEditorKey.database_row): YDatabaseRow;
|
||||
get (key: YjsEditorKey.database_row): YDatabaseRow;
|
||||
}
|
||||
|
||||
export interface YFolder extends Y.Map<unknown> {
|
||||
get(key: YjsFolderKey.views): YViews;
|
||||
get (key: YjsFolderKey.views): YViews;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/unified-signatures
|
||||
get(key: YjsFolderKey.meta): YFolderMeta;
|
||||
get (key: YjsFolderKey.meta): YFolderMeta;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/unified-signatures
|
||||
get(key: YjsFolderKey.relation): YFolderRelation;
|
||||
get (key: YjsFolderKey.relation): YFolderRelation;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/unified-signatures
|
||||
get(key: YjsFolderKey.section): YFolderSection;
|
||||
get (key: YjsFolderKey.section): YFolderSection;
|
||||
}
|
||||
|
||||
export interface YViews extends Y.Map<unknown> {
|
||||
get(key: ViewId): YView;
|
||||
get (key: ViewId): YView;
|
||||
}
|
||||
|
||||
export interface YView extends Y.Map<unknown> {
|
||||
get(key: YjsFolderKey.id): ViewId;
|
||||
get (key: YjsFolderKey.id): ViewId;
|
||||
|
||||
get(key: YjsFolderKey.bid): string;
|
||||
get (key: YjsFolderKey.bid): string;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/unified-signatures
|
||||
get(key: YjsFolderKey.name): string;
|
||||
get (key: YjsFolderKey.name): string;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/unified-signatures
|
||||
get(key: YjsFolderKey.icon | YjsFolderKey.extra): string;
|
||||
get (key: YjsFolderKey.icon | YjsFolderKey.extra): string;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/unified-signatures
|
||||
get(key: YjsFolderKey.layout): string;
|
||||
get (key: YjsFolderKey.layout): string;
|
||||
}
|
||||
|
||||
export interface YFolderRelation extends Y.Map<unknown> {
|
||||
get(key: ViewId): Y.Array<ViewId>;
|
||||
get (key: ViewId): Y.Array<ViewId>;
|
||||
}
|
||||
|
||||
export interface YFolderMeta extends Y.Map<unknown> {
|
||||
get(key: YjsFolderKey.current_view | YjsFolderKey.current_workspace): string;
|
||||
get (key: YjsFolderKey.current_view | YjsFolderKey.current_workspace): string;
|
||||
}
|
||||
|
||||
export interface YFolderSection extends Y.Map<unknown> {
|
||||
get(key: YjsFolderKey.favorite | YjsFolderKey.private | YjsFolderKey.recent | YjsFolderKey.trash): YFolderSectionItem;
|
||||
get (key: YjsFolderKey.favorite | YjsFolderKey.private | YjsFolderKey.recent | YjsFolderKey.trash): YFolderSectionItem;
|
||||
}
|
||||
|
||||
export interface YFolderSectionItem extends Y.Map<unknown> {
|
||||
get(key: string): Y.Array<unknown>;
|
||||
get (key: string): Y.Array<unknown>;
|
||||
}
|
||||
|
||||
export interface YDocument extends Y.Map<unknown> {
|
||||
get(key: YjsEditorKey.blocks | YjsEditorKey.page_id | YjsEditorKey.meta): YBlocks | YMeta | string;
|
||||
get (key: YjsEditorKey.blocks | YjsEditorKey.page_id | YjsEditorKey.meta): YBlocks | YMeta | string;
|
||||
}
|
||||
|
||||
export interface YBlocks extends Y.Map<unknown> {
|
||||
get(key: BlockId): YBlock;
|
||||
get (key: BlockId): YBlock;
|
||||
}
|
||||
|
||||
export interface YBlock extends Y.Map<unknown> {
|
||||
get(key: YjsEditorKey.block_id | YjsEditorKey.block_parent): BlockId;
|
||||
get (key: YjsEditorKey.block_id | YjsEditorKey.block_parent): BlockId;
|
||||
|
||||
get(key: YjsEditorKey.block_type): BlockType;
|
||||
get (key: YjsEditorKey.block_type): BlockType;
|
||||
|
||||
get(key: YjsEditorKey.block_data): string;
|
||||
get (key: YjsEditorKey.block_data): string;
|
||||
|
||||
get(key: YjsEditorKey.block_children): ChildrenId;
|
||||
get (key: YjsEditorKey.block_children): ChildrenId;
|
||||
|
||||
get(key: YjsEditorKey.block_external_id): ExternalId;
|
||||
get (key: YjsEditorKey.block_external_id): ExternalId;
|
||||
}
|
||||
|
||||
export interface YMeta extends Y.Map<unknown> {
|
||||
get(key: YjsEditorKey.children_map | YjsEditorKey.text_map): YChildrenMap | YTextMap;
|
||||
get (key: YjsEditorKey.children_map | YjsEditorKey.text_map): YChildrenMap | YTextMap;
|
||||
}
|
||||
|
||||
export interface YChildrenMap extends Y.Map<unknown> {
|
||||
get(key: ChildrenId): Y.Array<BlockId>;
|
||||
get (key: ChildrenId): Y.Array<BlockId>;
|
||||
}
|
||||
|
||||
export interface YTextMap extends Y.Map<unknown> {
|
||||
get(key: ExternalId): Y.Text;
|
||||
get (key: ExternalId): Y.Text;
|
||||
}
|
||||
|
||||
export interface YDatabase extends Y.Map<unknown> {
|
||||
get(key: YjsDatabaseKey.views): YDatabaseViews;
|
||||
get (key: YjsDatabaseKey.views): YDatabaseViews;
|
||||
|
||||
get(key: YjsDatabaseKey.metas): YDatabaseMetas;
|
||||
get (key: YjsDatabaseKey.metas): YDatabaseMetas;
|
||||
|
||||
get(key: YjsDatabaseKey.fields): YDatabaseFields;
|
||||
get (key: YjsDatabaseKey.fields): YDatabaseFields;
|
||||
|
||||
get(key: YjsDatabaseKey.id): string;
|
||||
get (key: YjsDatabaseKey.id): string;
|
||||
}
|
||||
|
||||
export interface YDatabaseViews extends Y.Map<YDatabaseView> {
|
||||
get(key: ViewId): YDatabaseView;
|
||||
get (key: ViewId): YDatabaseView;
|
||||
}
|
||||
|
||||
export type DatabaseId = string;
|
||||
@ -431,32 +444,32 @@ export enum DatabaseViewLayout {
|
||||
}
|
||||
|
||||
export interface YDatabaseView extends Y.Map<unknown> {
|
||||
get(key: YjsDatabaseKey.database_id): DatabaseId;
|
||||
get (key: YjsDatabaseKey.database_id): DatabaseId;
|
||||
|
||||
get(key: YjsDatabaseKey.name): string;
|
||||
get (key: YjsDatabaseKey.name): string;
|
||||
|
||||
get(key: YjsDatabaseKey.created_at): CreatedAt;
|
||||
get (key: YjsDatabaseKey.created_at): CreatedAt;
|
||||
|
||||
get(key: YjsDatabaseKey.modified_at): ModifiedAt;
|
||||
get (key: YjsDatabaseKey.modified_at): ModifiedAt;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/unified-signatures
|
||||
get(key: YjsDatabaseKey.layout): string;
|
||||
get (key: YjsDatabaseKey.layout): string;
|
||||
|
||||
get(key: YjsDatabaseKey.layout_settings): YDatabaseLayoutSettings;
|
||||
get (key: YjsDatabaseKey.layout_settings): YDatabaseLayoutSettings;
|
||||
|
||||
get(key: YjsDatabaseKey.filters): YDatabaseFilters;
|
||||
get (key: YjsDatabaseKey.filters): YDatabaseFilters;
|
||||
|
||||
get(key: YjsDatabaseKey.groups): YDatabaseGroups;
|
||||
get (key: YjsDatabaseKey.groups): YDatabaseGroups;
|
||||
|
||||
get(key: YjsDatabaseKey.sorts): YDatabaseSorts;
|
||||
get (key: YjsDatabaseKey.sorts): YDatabaseSorts;
|
||||
|
||||
get(key: YjsDatabaseKey.field_settings): YDatabaseFieldSettings;
|
||||
get (key: YjsDatabaseKey.field_settings): YDatabaseFieldSettings;
|
||||
|
||||
get(key: YjsDatabaseKey.field_orders): YDatabaseFieldOrders;
|
||||
get (key: YjsDatabaseKey.field_orders): YDatabaseFieldOrders;
|
||||
|
||||
get(key: YjsDatabaseKey.row_orders): YDatabaseRowOrders;
|
||||
get (key: YjsDatabaseKey.row_orders): YDatabaseRowOrders;
|
||||
|
||||
get(key: YjsDatabaseKey.calculations): YDatabaseCalculations;
|
||||
get (key: YjsDatabaseKey.calculations): YDatabaseCalculations;
|
||||
}
|
||||
|
||||
export type YDatabaseFieldOrders = Y.Array<unknown>; // [ { id: FieldId } ]
|
||||
@ -477,128 +490,128 @@ export type GroupId = string;
|
||||
|
||||
export interface YDatabaseLayoutSettings extends Y.Map<unknown> {
|
||||
// DatabaseViewLayout.Board
|
||||
get(key: '1'): YDatabaseBoardLayoutSetting;
|
||||
get (key: '1'): YDatabaseBoardLayoutSetting;
|
||||
|
||||
// DatabaseViewLayout.Calendar
|
||||
get(key: '2'): YDatabaseCalendarLayoutSetting;
|
||||
get (key: '2'): YDatabaseCalendarLayoutSetting;
|
||||
}
|
||||
|
||||
export interface YDatabaseBoardLayoutSetting extends Y.Map<unknown> {
|
||||
get(key: YjsDatabaseKey.hide_ungrouped_column | YjsDatabaseKey.collapse_hidden_groups): boolean;
|
||||
get (key: YjsDatabaseKey.hide_ungrouped_column | YjsDatabaseKey.collapse_hidden_groups): boolean;
|
||||
}
|
||||
|
||||
export interface YDatabaseCalendarLayoutSetting extends Y.Map<unknown> {
|
||||
get(key: YjsDatabaseKey.first_day_of_week | YjsDatabaseKey.field_id | YjsDatabaseKey.layout_ty): string;
|
||||
get (key: YjsDatabaseKey.first_day_of_week | YjsDatabaseKey.field_id | YjsDatabaseKey.layout_ty): string;
|
||||
|
||||
get(key: YjsDatabaseKey.show_week_numbers | YjsDatabaseKey.show_weekends): boolean;
|
||||
get (key: YjsDatabaseKey.show_week_numbers | YjsDatabaseKey.show_weekends): boolean;
|
||||
}
|
||||
|
||||
export interface YDatabaseGroup extends Y.Map<unknown> {
|
||||
get(key: YjsDatabaseKey.id): GroupId;
|
||||
get (key: YjsDatabaseKey.id): GroupId;
|
||||
|
||||
get(key: YjsDatabaseKey.field_id): FieldId;
|
||||
get (key: YjsDatabaseKey.field_id): FieldId;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/unified-signatures
|
||||
get(key: YjsDatabaseKey.content): string;
|
||||
get (key: YjsDatabaseKey.content): string;
|
||||
|
||||
get(key: YjsDatabaseKey.groups): YDatabaseGroupColumns;
|
||||
get (key: YjsDatabaseKey.groups): YDatabaseGroupColumns;
|
||||
}
|
||||
|
||||
export type YDatabaseGroupColumns = Y.Array<YDatabaseGroupColumn>;
|
||||
|
||||
export interface YDatabaseGroupColumn extends Y.Map<unknown> {
|
||||
get(key: YjsDatabaseKey.id): string;
|
||||
get (key: YjsDatabaseKey.id): string;
|
||||
|
||||
get(key: YjsDatabaseKey.visible): boolean;
|
||||
get (key: YjsDatabaseKey.visible): boolean;
|
||||
}
|
||||
|
||||
export interface YDatabaseRowOrder extends Y.Map<unknown> {
|
||||
get(key: YjsDatabaseKey.id): SortId;
|
||||
get (key: YjsDatabaseKey.id): SortId;
|
||||
|
||||
get(key: YjsDatabaseKey.height): number;
|
||||
get (key: YjsDatabaseKey.height): number;
|
||||
}
|
||||
|
||||
export interface YDatabaseSort extends Y.Map<unknown> {
|
||||
get(key: YjsDatabaseKey.id): SortId;
|
||||
get (key: YjsDatabaseKey.id): SortId;
|
||||
|
||||
get(key: YjsDatabaseKey.field_id): FieldId;
|
||||
get (key: YjsDatabaseKey.field_id): FieldId;
|
||||
|
||||
get(key: YjsDatabaseKey.condition): string;
|
||||
get (key: YjsDatabaseKey.condition): string;
|
||||
}
|
||||
|
||||
export type FilterId = string;
|
||||
|
||||
export interface YDatabaseFilter extends Y.Map<unknown> {
|
||||
get(key: YjsDatabaseKey.id): FilterId;
|
||||
get (key: YjsDatabaseKey.id): FilterId;
|
||||
|
||||
get(key: YjsDatabaseKey.field_id): FieldId;
|
||||
get (key: YjsDatabaseKey.field_id): FieldId;
|
||||
|
||||
get(key: YjsDatabaseKey.type | YjsDatabaseKey.condition | YjsDatabaseKey.content | YjsDatabaseKey.filter_type): string;
|
||||
get (key: YjsDatabaseKey.type | YjsDatabaseKey.condition | YjsDatabaseKey.content | YjsDatabaseKey.filter_type): string;
|
||||
}
|
||||
|
||||
export interface YDatabaseCalculation extends Y.Map<unknown> {
|
||||
get(key: YjsDatabaseKey.field_id): FieldId;
|
||||
get (key: YjsDatabaseKey.field_id): FieldId;
|
||||
|
||||
get(key: YjsDatabaseKey.id | YjsDatabaseKey.type | YjsDatabaseKey.calculation_value): string;
|
||||
get (key: YjsDatabaseKey.id | YjsDatabaseKey.type | YjsDatabaseKey.calculation_value): string;
|
||||
}
|
||||
|
||||
export interface YDatabaseFieldSettings extends Y.Map<unknown> {
|
||||
get(key: FieldId): YDatabaseFieldSetting;
|
||||
get (key: FieldId): YDatabaseFieldSetting;
|
||||
}
|
||||
|
||||
export interface YDatabaseFieldSetting extends Y.Map<unknown> {
|
||||
get(key: YjsDatabaseKey.visibility): string;
|
||||
get (key: YjsDatabaseKey.visibility): string;
|
||||
|
||||
get(key: YjsDatabaseKey.wrap): boolean;
|
||||
get (key: YjsDatabaseKey.wrap): boolean;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/unified-signatures
|
||||
get(key: YjsDatabaseKey.width): string;
|
||||
get (key: YjsDatabaseKey.width): string;
|
||||
}
|
||||
|
||||
export interface YDatabaseMetas extends Y.Map<unknown> {
|
||||
get(key: YjsDatabaseKey.iid): string;
|
||||
get (key: YjsDatabaseKey.iid): string;
|
||||
}
|
||||
|
||||
export interface YDatabaseFields extends Y.Map<YDatabaseField> {
|
||||
get(key: FieldId): YDatabaseField;
|
||||
get (key: FieldId): YDatabaseField;
|
||||
}
|
||||
|
||||
export interface YDatabaseField extends Y.Map<unknown> {
|
||||
get(key: YjsDatabaseKey.name): string;
|
||||
get (key: YjsDatabaseKey.name): string;
|
||||
|
||||
get(key: YjsDatabaseKey.id): FieldId;
|
||||
get (key: YjsDatabaseKey.id): FieldId;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/unified-signatures
|
||||
get(key: YjsDatabaseKey.type): string;
|
||||
get (key: YjsDatabaseKey.type): string;
|
||||
|
||||
get(key: YjsDatabaseKey.type_option): YDatabaseFieldTypeOption;
|
||||
get (key: YjsDatabaseKey.type_option): YDatabaseFieldTypeOption;
|
||||
|
||||
get(key: YjsDatabaseKey.is_primary): boolean;
|
||||
get (key: YjsDatabaseKey.is_primary): boolean;
|
||||
|
||||
get(key: YjsDatabaseKey.last_modified): LastModified;
|
||||
get (key: YjsDatabaseKey.last_modified): LastModified;
|
||||
}
|
||||
|
||||
export interface YDatabaseFieldTypeOption extends Y.Map<unknown> {
|
||||
// key is the field type
|
||||
get(key: string): YMapFieldTypeOption;
|
||||
get (key: string): YMapFieldTypeOption;
|
||||
}
|
||||
|
||||
export interface YMapFieldTypeOption extends Y.Map<unknown> {
|
||||
get(key: YjsDatabaseKey.content): string;
|
||||
get (key: YjsDatabaseKey.content): string;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/unified-signatures
|
||||
get(key: YjsDatabaseKey.data): string;
|
||||
get (key: YjsDatabaseKey.data): string;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/unified-signatures
|
||||
get(key: YjsDatabaseKey.time_format): string;
|
||||
get (key: YjsDatabaseKey.time_format): string;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/unified-signatures
|
||||
get(key: YjsDatabaseKey.date_format): string;
|
||||
get (key: YjsDatabaseKey.date_format): string;
|
||||
|
||||
get(key: YjsDatabaseKey.database_id): DatabaseId;
|
||||
get (key: YjsDatabaseKey.database_id): DatabaseId;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/unified-signatures
|
||||
get(key: YjsDatabaseKey.format): string;
|
||||
get (key: YjsDatabaseKey.format): string;
|
||||
}
|
||||
|
||||
export enum CollabType {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { GetViewRowsMap, LoadView, LoadViewMeta } from '@/application/collab.type';
|
||||
import { db } from '@/application/db';
|
||||
import { ViewMeta } from '@/application/db/tables/view_metas';
|
||||
import { AFConfigContext } from '@/components/app/AppConfig';
|
||||
import { AFConfigContext } from '@/components/app/app.hooks';
|
||||
import { useLiveQuery } from 'dexie-react-hooks';
|
||||
import { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
@ -9,6 +9,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
export interface PublishContextType {
|
||||
namespace: string;
|
||||
publishName: string;
|
||||
isTemplateThumb?: boolean;
|
||||
viewMeta?: ViewMeta;
|
||||
toView: (viewId: string) => Promise<void>;
|
||||
loadViewMeta: LoadViewMeta;
|
||||
@ -23,10 +24,12 @@ export const PublishProvider = ({
|
||||
children,
|
||||
namespace,
|
||||
publishName,
|
||||
isTemplateThumb,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
namespace: string;
|
||||
publishName: string;
|
||||
isTemplateThumb?: boolean;
|
||||
}) => {
|
||||
const viewMeta = useLiveQuery(async () => {
|
||||
const name = `${namespace}_${publishName}`;
|
||||
@ -87,7 +90,7 @@ export const PublishProvider = ({
|
||||
return Promise.reject(e);
|
||||
}
|
||||
},
|
||||
[navigate, service]
|
||||
[navigate, service],
|
||||
);
|
||||
|
||||
const loadViewMeta = useCallback(
|
||||
@ -124,7 +127,7 @@ export const PublishProvider = ({
|
||||
return Promise.reject(e);
|
||||
}
|
||||
},
|
||||
[service]
|
||||
[service],
|
||||
);
|
||||
|
||||
const getViewRowsMap = useCallback(
|
||||
@ -148,7 +151,7 @@ export const PublishProvider = ({
|
||||
return Promise.reject(e);
|
||||
}
|
||||
},
|
||||
[service]
|
||||
[service],
|
||||
);
|
||||
|
||||
const loadView = useCallback(
|
||||
@ -173,7 +176,7 @@ export const PublishProvider = ({
|
||||
return Promise.reject(e);
|
||||
}
|
||||
},
|
||||
[service]
|
||||
[service],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@ -195,6 +198,7 @@ export const PublishProvider = ({
|
||||
toView,
|
||||
namespace,
|
||||
publishName,
|
||||
isTemplateThumb,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
@ -202,6 +206,6 @@ export const PublishProvider = ({
|
||||
);
|
||||
};
|
||||
|
||||
export function usePublishContext() {
|
||||
export function usePublishContext () {
|
||||
return useContext(PublishContext);
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import { Fetcher, StrategyType } from '@/application/services/js-services/cache/
|
||||
// import { IndexeddbPersistence } from 'y-indexeddb';
|
||||
import * as Y from 'yjs';
|
||||
|
||||
export function collabTypeToDBType(type: CollabType) {
|
||||
export function collabTypeToDBType (type: CollabType) {
|
||||
switch (type) {
|
||||
case CollabType.Folder:
|
||||
return 'folder';
|
||||
@ -44,7 +44,7 @@ const collabSharedRootKeyMap = {
|
||||
[CollabType.Empty]: YjsEditorKey.empty,
|
||||
};
|
||||
|
||||
export function hasCollabCache(doc: YDoc) {
|
||||
export function hasCollabCache (doc: YDoc) {
|
||||
const data = doc.getMap(YjsEditorKey.data_section) as YSharedRoot;
|
||||
|
||||
return Object.values(collabSharedRootKeyMap).some((key) => {
|
||||
@ -52,7 +52,7 @@ export function hasCollabCache(doc: YDoc) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function hasViewMetaCache(name: string) {
|
||||
export async function hasViewMetaCache (name: string) {
|
||||
const data = await db.view_metas.get(name);
|
||||
|
||||
return !!data;
|
||||
@ -64,7 +64,7 @@ export async function getPublishViewMeta<
|
||||
child_views: PublishViewInfo[];
|
||||
ancestor_views: PublishViewInfo[];
|
||||
}
|
||||
>(
|
||||
> (
|
||||
fetcher: Fetcher<T>,
|
||||
{
|
||||
namespace,
|
||||
@ -73,7 +73,7 @@ export async function getPublishViewMeta<
|
||||
namespace: string;
|
||||
publishName: string;
|
||||
},
|
||||
strategy: StrategyType = StrategyType.CACHE_AND_NETWORK
|
||||
strategy: StrategyType = StrategyType.CACHE_AND_NETWORK,
|
||||
) {
|
||||
const name = `${namespace}_${publishName}`;
|
||||
const exist = await hasViewMetaCache(name);
|
||||
@ -124,7 +124,7 @@ export async function getPublishView<
|
||||
ancestor_views: PublishViewInfo[];
|
||||
};
|
||||
}
|
||||
>(
|
||||
> (
|
||||
fetcher: Fetcher<T>,
|
||||
{
|
||||
namespace,
|
||||
@ -133,7 +133,7 @@ export async function getPublishView<
|
||||
namespace: string;
|
||||
publishName: string;
|
||||
},
|
||||
strategy: StrategyType = StrategyType.CACHE_AND_NETWORK
|
||||
strategy: StrategyType = StrategyType.CACHE_AND_NETWORK,
|
||||
) {
|
||||
const name = `${namespace}_${publishName}`;
|
||||
const doc = await openCollabDB(name);
|
||||
@ -197,7 +197,7 @@ export async function revalidatePublishViewMeta<
|
||||
child_views: PublishViewInfo[];
|
||||
ancestor_views: PublishViewInfo[];
|
||||
}
|
||||
>(name: string, fetcher: Fetcher<T>) {
|
||||
> (name: string, fetcher: Fetcher<T>) {
|
||||
const { view, child_views, ancestor_views } = await fetcher();
|
||||
|
||||
const dbView = await db.view_metas.get(name);
|
||||
@ -211,7 +211,7 @@ export async function revalidatePublishViewMeta<
|
||||
visible_view_ids: dbView?.visible_view_ids ?? [],
|
||||
database_relations: dbView?.database_relations ?? {},
|
||||
},
|
||||
name
|
||||
name,
|
||||
);
|
||||
|
||||
return db.view_metas.get(name);
|
||||
@ -225,7 +225,7 @@ export async function revalidatePublishView<
|
||||
relations?: Record<DatabaseId, ViewId>;
|
||||
meta: PublishViewMetaData;
|
||||
}
|
||||
>(name: string, fetcher: Fetcher<T>, collab: YDoc, rowMapDoc: Y.Doc) {
|
||||
> (name: string, fetcher: Fetcher<T>, collab: YDoc, rowMapDoc: Y.Doc) {
|
||||
const { data, meta, rows, visibleViewIds = [], relations = {} } = await fetcher();
|
||||
|
||||
await db.view_metas.put(
|
||||
@ -237,7 +237,7 @@ export async function revalidatePublishView<
|
||||
visible_view_ids: visibleViewIds,
|
||||
database_relations: relations,
|
||||
},
|
||||
name
|
||||
name,
|
||||
);
|
||||
|
||||
if (rows) {
|
||||
@ -260,16 +260,14 @@ export async function revalidatePublishView<
|
||||
}
|
||||
}
|
||||
|
||||
console.log('====', data);
|
||||
|
||||
applyYDoc(collab, data);
|
||||
}
|
||||
|
||||
export async function deleteViewMeta(name: string) {
|
||||
export async function deleteViewMeta (name: string) {
|
||||
await db.view_metas.delete(name);
|
||||
}
|
||||
|
||||
export async function deleteView(name: string) {
|
||||
export async function deleteView (name: string) {
|
||||
console.log('deleteView', name);
|
||||
await deleteViewMeta(name);
|
||||
await closeCollabDB(name);
|
||||
|