Merge branch 'main' into add_workspace_settings

This commit is contained in:
Ian Su 2022-07-22 00:21:07 +08:00
commit 89644e88da
350 changed files with 10436 additions and 8482 deletions

26
.githooks/commit-msg Executable file
View File

@ -0,0 +1,26 @@
#!/bin/sh
#
# An example hook script to check the commit log message.
# Called by "git commit" with one argument, the name of the file
# that has the commit message. The hook should exit with non-zero
# status after issuing an appropriate message if it wants to stop the
# commit. The hook is allowed to edit the commit message file.
echo "Running the AppFlowy commit-msg hook."
# This example catches duplicate Signed-off-by lines.
test "" = "$(grep '^Signed-off-by: ' "$1" |
sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
echo >&2 Duplicate Signed-off-by lines.
exit 1
}
npx --no -- commitlint --edit $1
if [ $? -ne 0 ]
then
echo "Please fix your commit message to match AppFlowy coding standards"
exit 1
fi

4
.githooks/pre-commit Normal file → Executable file
View File

@ -1,5 +1,7 @@
#!/usr/bin/env bash
echo "Running local AppFlowy pre-commit hook."
#flutter format .
##https://gist.github.com/benmccallum/28e4f216d9d72f5965133e6c43aaff6e
limit=$(( 1 * 2**20 )) # 1MB
@ -31,4 +33,4 @@ for file in $( git diff-index --cached --name-only $against ); do
file_too_large $filename $file_size
exit 1;
fi
done
done

5
.githooks/pre-push Normal file → Executable file
View File

@ -1,15 +1,20 @@
#!/usr/bin/env bash
echo "Running local AppFlowy pre-push hook."
if [[ `git status --porcelain` ]]; then
printf "\e[31;1m%s\e[0m\n" 'This script needs to run against committed code only. Please commit or stash you changes.'
exit 1
fi
printf "\e[33;1m%s\e[0m\n" 'Running the Flutter analyzer'
flutter analyze
if [ $? -ne 0 ]; then
printf "\e[31;1m%s\e[0m\n" 'Flutter analyzer error'
exit 1
fi
printf "\e[33;1m%s\e[0m\n" 'Finished running the Flutter analyzer'
printf "\e[33;1m%s\e[0m\n" 'Running unit tests'

View File

@ -16,7 +16,7 @@ jobs:
os: [ubuntu-latest, macos-latest]
include:
- os: ubuntu-latest
flutter_profile: development-linux-x86
flutter_profile: development-linux-x86_64
- os: macos-latest
flutter_profile: development-mac-x86_64
runs-on: ${{ matrix.os }}
@ -82,4 +82,4 @@ jobs:
- name: Build
working-directory: frontend
run: |
cargo make --profile ${{ matrix.flutter_profile }} appflowy-dev
cargo make --profile ${{ matrix.flutter_profile }} appflowy-dev

View File

@ -42,7 +42,7 @@ jobs:
- name: Build FlowySDK
working-directory: frontend
run: |
cargo make --profile development-linux-x86 flowy-sdk-dev
cargo make --profile development-linux-x86_64 flowy-sdk-dev
- name: Code Generation
working-directory: frontend/app_flowy

View File

@ -56,7 +56,7 @@ jobs:
- name: Build FlowySDK
working-directory: frontend
run: |
cargo make --profile development-linux-x86 flowy-sdk-dev
cargo make --profile development-linux-x86_64 flowy-sdk-dev
- name: Code Generation
working-directory: frontend/app_flowy

View File

@ -67,7 +67,7 @@ jobs:
working-directory: frontend
run: |
flutter config --enable-linux-desktop
cargo make --env APP_VERSION=${{ github.ref_name }} --profile production-linux-x86 appflowy
cargo make --env APP_VERSION=${{ github.ref_name }} --profile production-linux-x86_64 appflowy
- name: Upload Release Asset
id: upload-release-asset

View File

@ -30,7 +30,7 @@ jobs:
- name: Build FlowySDK
working-directory: frontend
run: |
cargo make --profile development-linux-x86 flowy-sdk-dev
cargo make --profile development-linux-x86_64 flowy-sdk-dev
- run: rustup component add rustfmt
working-directory: frontend/rust-lib

6
.gitignore vendored
View File

@ -27,4 +27,8 @@ frontend/.vscode/*
!frontend/.vscode/tasks.json
!frontend/.vscode/launch.json
!frontend/.vscode/extensions.json
!frontend/.vscode/*.code-snippets
!frontend/.vscode/*.code-snippets
# Commit the highest level pubspec.lock, but ignore the others
pubspec.lock
!frontend/app_flowy/pubspec.lock

View File

@ -1,4 +0,0 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx --no -- commitlint --edit

View File

@ -1,35 +0,0 @@
[tasks.install-commitlint.mac]
script = [
"""
brew install npm
yarn install
yarn husky install
""",
]
script_runner = "@shell"
[tasks.install-commitlint.windows]
script = [
"""
echo "WIP"
""",
]
script_runner = "@duckscript"
[tasks.install-commitlint.linux]
script = [
"""
if command -v apt &> /dev/null
then
echo "Installing node.js and yarn (sudo apt install nodejs yarn)"
sudo apt install nodejs yarn
else
echo "Installing node.js and yarn (sudo pacman -S nodejs yarn)"
sudo pacman -S nodejs yarn
fi
yarn install
yarn husky install
""",
]
script_runner = "@shell"

View File

@ -1,2 +0,0 @@
brew 'sqlite3'
brew 'rustup-init'

View File

@ -1,14 +0,0 @@
.PHONY: flowy_dev_install flowy_clean
flowy_dev_install:
brew bundle
rustup-init -y --default-toolchain=stable
cargo install --force cargo-make
cargo install --force duckscript_cli
cargo make flowy_dev
flowy_clean:
sh ./scripts/clean.sh

View File

@ -8,6 +8,7 @@ extend = [
{ path = "scripts/makefile/env.toml" },
{ path = "scripts/makefile/flutter.toml" },
{ path = "scripts/makefile/tool.toml" },
{ path = "scripts/makefile/githooks.toml" },
]
[config]
@ -100,7 +101,7 @@ CRATE_TYPE = "cdylib"
SDK_EXT = "dll"
APP_ENVIRONMENT = "production"
[env.development-linux-x86]
[env.development-linux-x86_64]
TARGET_OS = "linux"
RUST_COMPILE_TARGET = "x86_64-unknown-linux-gnu"
BUILD_FLAG = "debug"
@ -109,7 +110,7 @@ FLUTTER_OUTPUT_DIR = "Debug"
SDK_EXT = "so"
LINUX_ARCH = "x64"
[env.production-linux-x86]
[env.production-linux-x86_64]
BUILD_FLAG = "release"
TARGET_OS = "linux"
RUST_COMPILE_TARGET = "x86_64-unknown-linux-gnu"

View File

@ -173,8 +173,8 @@
"includeTime": " Include time",
"dateFormatFriendly": "Month Day,Year",
"dateFormatISO": "Year-Month-Day",
"dateFormatLocal": "Month/Month/Day",
"dateFormatUS": "Month/Month/Day",
"dateFormatLocal": "Year/Month/Day",
"dateFormatUS": "Year/Month/Day",
"timeFormat": " Time format",
"invalidTimeFormat": "Invalid format",
"timeFormatTwelveHour": "12 hour",
@ -215,4 +215,4 @@
"timeHintTextInTwentyFourHour": "12:00"
}
}
}
}

View File

@ -8,7 +8,7 @@ import 'package:flowy_sdk/rust_stream.dart';
import 'notification_helper.dart';
// Grid
// GridPB
typedef GridNotificationCallback = void Function(GridNotification, Either<Uint8List, FlowyError>);
class GridNotificationParser extends NotificationParser<GridNotification, FlowyError> {

View File

@ -53,14 +53,14 @@ void _resolveHomeDeps(GetIt getIt) {
getIt.registerSingleton(MenuSharedState());
getIt.registerFactoryParam<UserListener, UserProfile, void>(
getIt.registerFactoryParam<UserListener, UserProfilePB, void>(
(user, _) => UserListener(userProfile: user),
);
//
getIt.registerLazySingleton<HomeStackManager>(() => HomeStackManager());
getIt.registerFactoryParam<WelcomeBloc, UserProfile, void>(
getIt.registerFactoryParam<WelcomeBloc, UserProfilePB, void>(
(user, _) => WelcomeBloc(
userService: UserService(userId: user.id),
userWorkspaceListener: UserWorkspaceListener(userProfile: user),
@ -69,21 +69,21 @@ void _resolveHomeDeps(GetIt getIt) {
// share
getIt.registerLazySingleton<ShareService>(() => ShareService());
getIt.registerFactoryParam<DocShareBloc, View, void>(
getIt.registerFactoryParam<DocShareBloc, ViewPB, void>(
(view, _) => DocShareBloc(view: view, service: getIt<ShareService>()));
}
void _resolveFolderDeps(GetIt getIt) {
//workspace
getIt.registerFactoryParam<WorkspaceListener, UserProfile, String>(
getIt.registerFactoryParam<WorkspaceListener, UserProfilePB, String>(
(user, workspaceId) => WorkspaceListener(user: user, workspaceId: workspaceId));
// View
getIt.registerFactoryParam<ViewListener, View, void>(
// ViewPB
getIt.registerFactoryParam<ViewListener, ViewPB, void>(
(view, _) => ViewListener(view: view),
);
getIt.registerFactoryParam<ViewBloc, View, void>(
getIt.registerFactoryParam<ViewBloc, ViewPB, void>(
(view, _) => ViewBloc(
view: view,
service: ViewService(),
@ -92,29 +92,29 @@ void _resolveFolderDeps(GetIt getIt) {
);
//Menu
getIt.registerFactoryParam<MenuBloc, UserProfile, String>(
getIt.registerFactoryParam<MenuBloc, UserProfilePB, String>(
(user, workspaceId) => MenuBloc(
workspaceId: workspaceId,
listener: getIt<WorkspaceListener>(param1: user, param2: workspaceId),
),
);
getIt.registerFactoryParam<MenuUserBloc, UserProfile, void>(
getIt.registerFactoryParam<MenuUserBloc, UserProfilePB, void>(
(user, _) => MenuUserBloc(user),
);
//Settings
getIt.registerFactoryParam<SettingsDialogBloc, UserProfile, void>(
getIt.registerFactoryParam<SettingsDialogBloc, UserProfilePB, void>(
(user, _) => SettingsDialogBloc(user),
);
//User
getIt.registerFactoryParam<SettingsUserViewBloc, UserProfile, void>(
getIt.registerFactoryParam<SettingsUserViewBloc, UserProfilePB, void>(
(user, _) => SettingsUserViewBloc(user),
);
// App
getIt.registerFactoryParam<AppBloc, App, void>(
// AppPB
getIt.registerFactoryParam<AppBloc, AppPB, void>(
(app, _) => AppBloc(
app: app,
appService: AppService(appId: app.id),
@ -135,7 +135,7 @@ void _resolveFolderDeps(GetIt getIt) {
void _resolveDocDeps(GetIt getIt) {
// Doc
getIt.registerFactoryParam<DocumentBloc, View, void>(
getIt.registerFactoryParam<DocumentBloc, ViewPB, void>(
(view, _) => DocumentBloc(
view: view,
service: DocumentService(),
@ -146,8 +146,8 @@ void _resolveDocDeps(GetIt getIt) {
}
void _resolveGridDeps(GetIt getIt) {
// Grid
getIt.registerFactoryParam<GridBloc, View, void>(
// GridPB
getIt.registerFactoryParam<GridBloc, ViewPB, void>(
(view, _) => GridBloc(view: view),
);
@ -165,31 +165,31 @@ void _resolveGridDeps(GetIt getIt) {
),
);
getIt.registerFactoryParam<TextCellBloc, GridCellContext, void>(
getIt.registerFactoryParam<TextCellBloc, GridCellController, void>(
(context, _) => TextCellBloc(
cellContext: context,
),
);
getIt.registerFactoryParam<SelectOptionCellBloc, GridSelectOptionCellContext, void>(
getIt.registerFactoryParam<SelectOptionCellBloc, GridSelectOptionCellController, void>(
(context, _) => SelectOptionCellBloc(
cellContext: context,
),
);
getIt.registerFactoryParam<NumberCellBloc, GridCellContext, void>(
getIt.registerFactoryParam<NumberCellBloc, GridCellController, void>(
(context, _) => NumberCellBloc(
cellContext: context,
),
);
getIt.registerFactoryParam<DateCellBloc, GridDateCellContext, void>(
getIt.registerFactoryParam<DateCellBloc, GridDateCellController, void>(
(context, _) => DateCellBloc(
cellContext: context,
),
);
getIt.registerFactoryParam<CheckboxCellBloc, GridCellContext, void>(
getIt.registerFactoryParam<CheckboxCellBloc, GridCellController, void>(
(cellData, _) => CheckboxCellBloc(
service: CellService(),
cellContext: cellData,

View File

@ -1,21 +1,21 @@
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show SignInPayload, SignUpPayload, UserProfile;
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show SignInPayloadPB, SignUpPayloadPB, UserProfilePB;
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
class AuthService {
Future<Either<UserProfile, FlowyError>> signIn({required String? email, required String? password}) {
Future<Either<UserProfilePB, FlowyError>> signIn({required String? email, required String? password}) {
//
final request = SignInPayload.create()
final request = SignInPayloadPB.create()
..email = email ?? ''
..password = password ?? '';
return UserEventSignIn(request).send();
}
Future<Either<UserProfile, FlowyError>> signUp(
Future<Either<UserProfilePB, FlowyError>> signUp(
{required String? name, required String? password, required String? email}) {
final request = SignUpPayload.create()
final request = SignUpPayloadPB.create()
..email = email ?? ''
..name = name ?? ''
..password = password ?? '';

View File

@ -2,7 +2,7 @@ import 'package:app_flowy/user/application/auth_service.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfile;
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfilePB;
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -69,7 +69,7 @@ class SignInState with _$SignInState {
required bool isSubmitting,
required Option<String> passwordError,
required Option<String> emailError,
required Option<Either<UserProfile, FlowyError>> successOrFail,
required Option<Either<UserProfilePB, FlowyError>> successOrFail,
}) = _SignInState;
factory SignInState.initial() => SignInState(

View File

@ -2,7 +2,7 @@ import 'package:app_flowy/user/application/auth_service.dart';
import 'package:dartz/dartz.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfile;
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfilePB;
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -120,7 +120,7 @@ class SignUpState with _$SignUpState {
required Option<String> passwordError,
required Option<String> repeatPasswordError,
required Option<String> emailError,
required Option<Either<UserProfile, FlowyError>> successOrFail,
required Option<Either<UserProfilePB, FlowyError>> successOrFail,
}) = _SignUpState;
factory SignUpState.initial() => SignUpState(

View File

@ -13,7 +13,7 @@ import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-user/dart_notification.pb.dart' as user;
import 'package:flowy_sdk/rust_stream.dart';
typedef UserProfileNotifyValue = Either<UserProfile, FlowyError>;
typedef UserProfileNotifyValue = Either<UserProfilePB, FlowyError>;
typedef AuthNotifyValue = Either<Unit, FlowyError>;
class UserListener {
@ -22,9 +22,9 @@ class UserListener {
PublishNotifier<UserProfileNotifyValue>? _profileNotifier = PublishNotifier();
UserNotificationParser? _userParser;
final UserProfile _userProfile;
final UserProfilePB _userProfile;
UserListener({
required UserProfile userProfile,
required UserProfilePB userProfile,
}) : _userProfile = userProfile;
void start({
@ -65,7 +65,7 @@ class UserListener {
break;
case user.UserNotification.UserProfileUpdated:
result.fold(
(payload) => _profileNotifier?.value = left(UserProfile.fromBuffer(payload)),
(payload) => _profileNotifier?.value = left(UserProfilePB.fromBuffer(payload)),
(error) => _profileNotifier?.value = right(error),
);
break;
@ -75,8 +75,8 @@ class UserListener {
}
}
typedef WorkspaceListNotifyValue = Either<List<Workspace>, FlowyError>;
typedef WorkspaceSettingNotifyValue = Either<CurrentWorkspaceSetting, FlowyError>;
typedef WorkspaceListNotifyValue = Either<List<WorkspacePB>, FlowyError>;
typedef WorkspaceSettingNotifyValue = Either<CurrentWorkspaceSettingPB, FlowyError>;
class UserWorkspaceListener {
PublishNotifier<AuthNotifyValue>? _authNotifier = PublishNotifier();
@ -84,10 +84,10 @@ class UserWorkspaceListener {
PublishNotifier<WorkspaceSettingNotifyValue>? _settingChangedNotifier = PublishNotifier();
FolderNotificationListener? _listener;
final UserProfile _userProfile;
final UserProfilePB _userProfile;
UserWorkspaceListener({
required UserProfile userProfile,
required UserProfilePB userProfile,
}) : _userProfile = userProfile;
void start({
@ -119,13 +119,13 @@ class UserWorkspaceListener {
case FolderNotification.UserDeleteWorkspace:
case FolderNotification.WorkspaceListUpdated:
result.fold(
(payload) => _workspacesChangedNotifier?.value = left(RepeatedWorkspace.fromBuffer(payload).items),
(payload) => _workspacesChangedNotifier?.value = left(RepeatedWorkspacePB.fromBuffer(payload).items),
(error) => _workspacesChangedNotifier?.value = right(error),
);
break;
case FolderNotification.WorkspaceSetting:
result.fold(
(payload) => _settingChangedNotifier?.value = left(CurrentWorkspaceSetting.fromBuffer(payload)),
(payload) => _settingChangedNotifier?.value = left(CurrentWorkspaceSettingPB.fromBuffer(payload)),
(error) => _settingChangedNotifier?.value = right(error),
);
break;

View File

@ -11,7 +11,7 @@ class UserService {
UserService({
required this.userId,
});
Future<Either<UserProfile, FlowyError>> getUserProfile({required String userId}) {
Future<Either<UserProfilePB, FlowyError>> getUserProfile({required String userId}) {
return UserEventGetUserProfile().send();
}
@ -20,7 +20,7 @@ class UserService {
String? password,
String? email,
}) {
var payload = UpdateUserProfilePayload.create()..id = userId;
var payload = UpdateUserProfilePayloadPB.create()..id = userId;
if (name != null) {
payload.name = name;
@ -49,8 +49,8 @@ class UserService {
return UserEventInitUser().send();
}
Future<Either<List<Workspace>, FlowyError>> getWorkspaces() {
final request = WorkspaceId.create();
Future<Either<List<WorkspacePB>, FlowyError>> getWorkspaces() {
final request = WorkspaceIdPB.create();
return FolderEventReadWorkspaces(request).send().then((result) {
return result.fold(
@ -60,8 +60,8 @@ class UserService {
});
}
Future<Either<Workspace, FlowyError>> openWorkspace(String workspaceId) {
final request = WorkspaceId.create()..value = workspaceId;
Future<Either<WorkspacePB, FlowyError>> openWorkspace(String workspaceId) {
final request = WorkspaceIdPB.create()..value = workspaceId;
return FolderEventOpenWorkspace(request).send().then((result) {
return result.fold(
(workspace) => left(workspace),
@ -70,8 +70,8 @@ class UserService {
});
}
Future<Either<Workspace, FlowyError>> createWorkspace(String name, String desc) {
final request = CreateWorkspacePayload.create()
Future<Either<WorkspacePB, FlowyError>> createWorkspace(String name, String desc) {
final request = CreateWorkspacePayloadPB.create()
..name = name
..desc = desc;
return FolderEventCreateWorkspace(request).send().then((result) {

View File

@ -5,11 +5,11 @@ import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-user/user_setting.pb.dart';
class UserSettingsService {
Future<AppearanceSettings> getAppearanceSettings() async {
Future<AppearanceSettingsPB> getAppearanceSettings() async {
final result = await UserEventGetAppearanceSetting().send();
return result.fold(
(AppearanceSettings setting) {
(AppearanceSettingsPB setting) {
return setting;
},
(error) {
@ -18,7 +18,7 @@ class UserSettingsService {
);
}
Future<Either<Unit, FlowyError>> setAppearanceSettings(AppearanceSettings settings) {
Future<Either<Unit, FlowyError>> setAppearanceSettings(AppearanceSettingsPB settings) {
return UserEventSetAppearanceSetting(settings).send();
}
}

View File

@ -1,11 +1,11 @@
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfile;
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfilePB;
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'auth_state.freezed.dart';
@freezed
class AuthState with _$AuthState {
const factory AuthState.authenticated(UserProfile userProfile) = Authenticated;
const factory AuthState.authenticated(UserProfilePB userProfile) = Authenticated;
const factory AuthState.unauthenticated(FlowyError error) = Unauthenticated;
const factory AuthState.initial() = _Initial;
}

View File

@ -7,7 +7,7 @@ import 'package:app_flowy/user/presentation/welcome_screen.dart';
import 'package:app_flowy/workspace/presentation/home/home_screen.dart';
import 'package:flowy_infra/time/duration.dart';
import 'package:flowy_infra_ui/widget/route/animation.dart';
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfile;
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfilePB;
import 'package:flowy_sdk/protobuf/flowy-folder/protobuf.dart';
import 'package:flutter/material.dart';
@ -16,7 +16,7 @@ class AuthRouter {
// TODO: implement showForgetPasswordScreen
}
void pushWelcomeScreen(BuildContext context, UserProfile userProfile) {
void pushWelcomeScreen(BuildContext context, UserProfilePB userProfile) {
getIt<SplashRoute>().pushWelcomeScreen(context, userProfile);
}
@ -28,7 +28,7 @@ class AuthRouter {
);
}
void pushHomeScreen(BuildContext context, UserProfile profile, CurrentWorkspaceSetting workspaceSetting) {
void pushHomeScreen(BuildContext context, UserProfilePB profile, CurrentWorkspaceSettingPB workspaceSetting) {
Navigator.push(
context,
PageRoutes.fade(() => HomeScreen(profile, workspaceSetting), RouteDurations.slow.inMilliseconds * .001),
@ -37,7 +37,7 @@ class AuthRouter {
}
class SplashRoute {
Future<void> pushWelcomeScreen(BuildContext context, UserProfile userProfile) async {
Future<void> pushWelcomeScreen(BuildContext context, UserProfilePB userProfile) async {
final screen = WelcomeScreen(userProfile: userProfile);
final workspaceId = await Navigator.of(context).push(
PageRoutes.fade(
@ -49,7 +49,7 @@ class SplashRoute {
pushHomeScreen(context, userProfile, workspaceId);
}
void pushHomeScreen(BuildContext context, UserProfile userProfile, CurrentWorkspaceSetting workspaceSetting) {
void pushHomeScreen(BuildContext context, UserProfilePB userProfile, CurrentWorkspaceSettingPB workspaceSetting) {
Navigator.push(
context,
PageRoutes.fade(() => HomeScreen(userProfile, workspaceSetting), RouteDurations.slow.inMilliseconds * .001),

View File

@ -10,7 +10,7 @@ import 'package:flowy_infra_ui/widget/rounded_input_field.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flowy_infra_ui/style_widget/snap_bar.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfile;
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfilePB;
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:dartz/dartz.dart';
@ -39,7 +39,7 @@ class SignInScreen extends StatelessWidget {
);
}
void _handleSuccessOrFail(Either<UserProfile, FlowyError> result, BuildContext context) {
void _handleSuccessOrFail(Either<UserProfilePB, FlowyError> result, BuildContext context) {
result.fold(
(user) => router.pushWelcomeScreen(context, user),
(error) => showSnapBar(context, error.msg),

View File

@ -8,7 +8,7 @@ import 'package:flowy_infra_ui/widget/rounded_button.dart';
import 'package:flowy_infra_ui/widget/rounded_input_field.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfile;
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfilePB;
import 'package:flowy_infra_ui/style_widget/snap_bar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -36,7 +36,7 @@ class SignUpScreen extends StatelessWidget {
);
}
void _handleSuccessOrFail(BuildContext context, Either<UserProfile, FlowyError> result) {
void _handleSuccessOrFail(BuildContext context, Either<UserProfilePB, FlowyError> result) {
result.fold(
(user) => router.pushWelcomeScreen(context, user),
(error) => showSnapBar(context, error.msg),

View File

@ -116,8 +116,8 @@ class _SkipLogInScreenState extends State<SkipLogInScreen> {
void _openCurrentWorkspace(
BuildContext context,
UserProfile user,
dartz.Either<CurrentWorkspaceSetting, FlowyError> workspacesOrError,
UserProfilePB user,
dartz.Either<CurrentWorkspaceSettingPB, FlowyError> workspacesOrError,
) {
workspacesOrError.fold(
(workspaceSetting) {

View File

@ -12,7 +12,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:app_flowy/generated/locale_keys.g.dart';
class WelcomeScreen extends StatelessWidget {
final UserProfile userProfile;
final UserProfilePB userProfile;
const WelcomeScreen({
Key? key,
required this.userProfile,
@ -65,7 +65,7 @@ class WelcomeScreen extends StatelessWidget {
);
}
Widget _renderList(List<Workspace> workspaces) {
Widget _renderList(List<WorkspacePB> workspaces) {
return Expanded(
child: StyledListView(
itemBuilder: (BuildContext context, int index) {
@ -80,7 +80,7 @@ class WelcomeScreen extends StatelessWidget {
);
}
void _handleOnPress(BuildContext context, Workspace workspace) {
void _handleOnPress(BuildContext context, WorkspacePB workspace) {
context.read<WelcomeBloc>().add(WelcomeEvent.openWorkspace(workspace));
Navigator.of(context).pop(workspace.id);
@ -88,8 +88,8 @@ class WelcomeScreen extends StatelessWidget {
}
class WorkspaceItem extends StatelessWidget {
final Workspace workspace;
final void Function(Workspace workspace) onPressed;
final WorkspacePB workspace;
final void Function(WorkspacePB workspace) onPressed;
const WorkspaceItem({Key? key, required this.workspace, required this.onPressed}) : super(key: key);
@override

View File

@ -18,7 +18,7 @@ import 'package:dartz/dartz.dart';
part 'app_bloc.freezed.dart';
class AppBloc extends Bloc<AppEvent, AppState> {
final App app;
final AppPB app;
final AppService appService;
final AppListener appListener;
@ -103,7 +103,7 @@ class AppBloc extends Bloc<AppEvent, AppState> {
return super.close();
}
Future<void> _didReceiveViewUpdated(List<View> views, Emitter<AppState> emit) async {
Future<void> _didReceiveViewUpdated(List<ViewPB> views, Emitter<AppState> emit) async {
final latestCreatedView = state.latestCreatedView;
AppState newState = state.copyWith(views: views);
if (latestCreatedView != null) {
@ -139,20 +139,20 @@ class AppEvent with _$AppEvent {
) = CreateView;
const factory AppEvent.delete() = Delete;
const factory AppEvent.rename(String newName) = Rename;
const factory AppEvent.didReceiveViewUpdated(List<View> views) = ReceiveViews;
const factory AppEvent.appDidUpdate(App app) = AppDidUpdate;
const factory AppEvent.didReceiveViewUpdated(List<ViewPB> views) = ReceiveViews;
const factory AppEvent.appDidUpdate(AppPB app) = AppDidUpdate;
}
@freezed
class AppState with _$AppState {
const factory AppState({
required App app,
required List<View> views,
View? latestCreatedView,
required AppPB app,
required List<ViewPB> views,
ViewPB? latestCreatedView,
required Either<Unit, FlowyError> successOrFailure,
}) = _AppState;
factory AppState.initial(App app) => AppState(
factory AppState.initial(AppPB app) => AppState(
app: app,
views: [],
successOrFailure: left(unit),
@ -161,8 +161,8 @@ class AppState with _$AppState {
class AppViewDataContext extends ChangeNotifier {
final String appId;
final ValueNotifier<List<View>> _viewsNotifier = ValueNotifier([]);
final ValueNotifier<View?> _selectedViewNotifier = ValueNotifier(null);
final ValueNotifier<List<ViewPB>> _viewsNotifier = ValueNotifier([]);
final ValueNotifier<ViewPB?> _selectedViewNotifier = ValueNotifier(null);
VoidCallback? _menuSharedStateListener;
ExpandableController expandController = ExpandableController(initialExpanded: false);
@ -173,7 +173,7 @@ class AppViewDataContext extends ChangeNotifier {
});
}
VoidCallback addSelectedViewChangeListener(void Function(View?) callback) {
VoidCallback addSelectedViewChangeListener(void Function(ViewPB?) callback) {
listener() {
callback(_selectedViewNotifier.value);
}
@ -186,7 +186,7 @@ class AppViewDataContext extends ChangeNotifier {
_selectedViewNotifier.removeListener(listener);
}
void _setLatestView(View? view) {
void _setLatestView(ViewPB? view) {
view?.freeze();
if (_selectedViewNotifier.value != view) {
@ -196,9 +196,9 @@ class AppViewDataContext extends ChangeNotifier {
}
}
View? get selectedView => _selectedViewNotifier.value;
ViewPB? get selectedView => _selectedViewNotifier.value;
set views(List<View> views) {
set views(List<ViewPB> views) {
if (_viewsNotifier.value != views) {
_viewsNotifier.value = views;
_expandIfNeed();
@ -206,9 +206,9 @@ class AppViewDataContext extends ChangeNotifier {
}
}
UnmodifiableListView<View> get views => UnmodifiableListView(_viewsNotifier.value);
UnmodifiableListView<ViewPB> get views => UnmodifiableListView(_viewsNotifier.value);
VoidCallback addViewsChangeListener(void Function(UnmodifiableListView<View>) callback) {
VoidCallback addViewsChangeListener(void Function(UnmodifiableListView<ViewPB>) callback) {
listener() {
callback(views);
}

View File

@ -10,8 +10,8 @@ import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/dart_notification.pb.dart';
import 'package:flowy_sdk/rust_stream.dart';
typedef AppDidUpdateCallback = void Function(App app);
typedef ViewsDidChangeCallback = void Function(Either<List<View>, FlowyError> viewsOrFailed);
typedef AppDidUpdateCallback = void Function(AppPB app);
typedef ViewsDidChangeCallback = void Function(Either<List<ViewPB>, FlowyError> viewsOrFailed);
class AppListener {
StreamSubscription<SubscribeObject>? _subscription;
@ -37,7 +37,7 @@ class AppListener {
if (_viewsChanged != null) {
result.fold(
(payload) {
final repeatedView = RepeatedView.fromBuffer(payload);
final repeatedView = RepeatedViewPB.fromBuffer(payload);
_viewsChanged!(left(repeatedView.items));
},
(error) => _viewsChanged!(right(error)),
@ -48,7 +48,7 @@ class AppListener {
if (_updated != null) {
result.fold(
(payload) {
final app = App.fromBuffer(payload);
final app = AppPB.fromBuffer(payload);
_updated!(app);
},
(error) => Log.error(error),

View File

@ -14,20 +14,20 @@ class AppService {
required this.appId,
});
Future<Either<App, FlowyError>> getAppDesc({required String appId}) {
final payload = AppId.create()..value = appId;
Future<Either<AppPB, FlowyError>> getAppDesc({required String appId}) {
final payload = AppIdPB.create()..value = appId;
return FolderEventReadApp(payload).send();
}
Future<Either<View, FlowyError>> createView({
Future<Either<ViewPB, FlowyError>> createView({
required String appId,
required String name,
required String desc,
required PluginDataType dataType,
required PluginType pluginType,
}) {
final payload = CreateViewPayload.create()
final payload = CreateViewPayloadPB.create()
..belongToId = appId
..name = name
..desc = desc
@ -37,8 +37,8 @@ class AppService {
return FolderEventCreateView(payload).send();
}
Future<Either<List<View>, FlowyError>> getViews({required String appId}) {
final payload = AppId.create()..value = appId;
Future<Either<List<ViewPB>, FlowyError>> getViews({required String appId}) {
final payload = AppIdPB.create()..value = appId;
return FolderEventReadApp(payload).send().then((result) {
return result.fold(
@ -49,12 +49,12 @@ class AppService {
}
Future<Either<Unit, FlowyError>> delete({required String appId}) {
final request = AppId.create()..value = appId;
final request = AppIdPB.create()..value = appId;
return FolderEventDeleteApp(request).send();
}
Future<Either<Unit, FlowyError>> updateApp({required String appId, String? name}) {
UpdateAppPayload payload = UpdateAppPayload.create()..appId = appId;
UpdateAppPayloadPB payload = UpdateAppPayloadPB.create()..appId = appId;
if (name != null) {
payload.name = name;
@ -67,7 +67,7 @@ class AppService {
required int fromIndex,
required int toIndex,
}) {
final payload = MoveFolderItemPayload.create()
final payload = MoveFolderItemPayloadPB.create()
..itemId = viewId
..from = fromIndex
..to = toIndex

View File

@ -9,7 +9,7 @@ import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
class AppearanceSettingModel extends ChangeNotifier with EquatableMixin {
AppearanceSettings setting;
AppearanceSettingsPB setting;
AppTheme _theme;
Locale _locale;
Timer? _saveOperation;

View File

@ -17,7 +17,7 @@ part 'doc_bloc.freezed.dart';
typedef FlutterQuillDocument = Document;
class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
final View view;
final ViewPB view;
final DocumentService service;
final ViewListener listener;

View File

@ -6,24 +6,24 @@ import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-sync/text_block.pb.dart';
class DocumentService {
Future<Either<TextBlockDelta, FlowyError>> openDocument({
Future<Either<TextBlockDeltaPB, FlowyError>> openDocument({
required String docId,
}) async {
await FolderEventSetLatestView(ViewId(value: docId)).send();
await FolderEventSetLatestView(ViewIdPB(value: docId)).send();
final payload = TextBlockId(value: docId);
final payload = TextBlockIdPB(value: docId);
return TextBlockEventGetBlockData(payload).send();
}
Future<Either<TextBlockDelta, FlowyError>> composeDelta({required String docId, required String data}) {
final payload = TextBlockDelta.create()
Future<Either<TextBlockDeltaPB, FlowyError>> composeDelta({required String docId, required String data}) {
final payload = TextBlockDeltaPB.create()
..blockId = docId
..deltaStr = data;
return TextBlockEventApplyDelta(payload).send();
}
Future<Either<Unit, FlowyError>> closeDocument({required String docId}) {
final request = ViewId(value: docId);
final request = ViewIdPB(value: docId);
return FolderEventCloseView(request).send();
}
}

View File

@ -13,7 +13,7 @@ part 'share_bloc.freezed.dart';
class DocShareBloc extends Bloc<DocShareEvent, DocShareState> {
ShareService service;
View view;
ViewPB view;
DocShareBloc({required this.view, required this.service}) : super(const DocShareState.initial()) {
on<DocShareEvent>((event, emit) async {
await event.map(
@ -33,7 +33,7 @@ class DocShareBloc extends Bloc<DocShareEvent, DocShareState> {
});
}
ExportData _convertDeltaToMarkdown(ExportData value) {
ExportDataPB _convertDeltaToMarkdown(ExportDataPB value) {
final result = deltaToMarkdown(value.data);
value.data = result;
writeFile(result);
@ -73,5 +73,5 @@ class DocShareEvent with _$DocShareEvent {
class DocShareState with _$DocShareState {
const factory DocShareState.initial() = _Initial;
const factory DocShareState.loading() = _Loading;
const factory DocShareState.finish(Either<ExportData, FlowyError> successOrFail) = _Finish;
const factory DocShareState.finish(Either<ExportDataPB, FlowyError> successOrFail) = _Finish;
}

View File

@ -5,23 +5,23 @@ import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-text-block/protobuf.dart';
class ShareService {
Future<Either<ExportData, FlowyError>> export(String docId, ExportType type) {
final request = ExportPayload.create()
Future<Either<ExportDataPB, FlowyError>> export(String docId, ExportType type) {
final request = ExportPayloadPB.create()
..viewId = docId
..exportType = type;
return TextBlockEventExportDocument(request).send();
}
Future<Either<ExportData, FlowyError>> exportText(String docId) {
Future<Either<ExportDataPB, FlowyError>> exportText(String docId) {
return export(docId, ExportType.Text);
}
Future<Either<ExportData, FlowyError>> exportMarkdown(String docId) {
Future<Either<ExportDataPB, FlowyError>> exportMarkdown(String docId) {
return export(docId, ExportType.Markdown);
}
Future<Either<ExportData, FlowyError>> exportURL(String docId) {
Future<Either<ExportDataPB, FlowyError>> exportURL(String docId) {
return export(docId, ExportType.Link);
}
}

View File

@ -6,24 +6,25 @@ import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
import 'block_listener.dart';
class GridBlockCacheService {
/// Read https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/frontend/grid for more information
class GridBlockCache {
final String gridId;
final GridBlock block;
late GridRowCacheService _rowCache;
final GridBlockPB block;
late GridRowCache _rowCache;
late GridBlockListener _listener;
List<GridRow> get rows => _rowCache.rows;
GridRowCacheService get rowCache => _rowCache;
List<GridRowInfo> get rows => _rowCache.rows;
GridRowCache get rowCache => _rowCache;
GridBlockCacheService({
GridBlockCache({
required this.gridId,
required this.block,
required GridFieldCache fieldCache,
}) {
_rowCache = GridRowCacheService(
_rowCache = GridRowCache(
gridId: gridId,
block: block,
delegate: GridRowCacheDelegateImpl(fieldCache),
notifier: GridRowCacheFieldNotifierImpl(fieldCache),
);
_listener = GridBlockListener(blockId: block.id);

View File

@ -7,7 +7,7 @@ import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
typedef GridBlockUpdateNotifierValue = Either<List<GridRowsChangeset>, FlowyError>;
typedef GridBlockUpdateNotifierValue = Either<List<GridBlockChangesetPB>, FlowyError>;
class GridBlockListener {
final String blockId;
@ -33,7 +33,7 @@ class GridBlockListener {
switch (ty) {
case GridNotification.DidUpdateGridBlock:
result.fold(
(payload) => _rowsUpdateNotifier?.value = left([GridRowsChangeset.fromBuffer(payload)]),
(payload) => _rowsUpdateNotifier?.value = left([GridBlockChangesetPB.fromBuffer(payload)]),
(error) => _rowsUpdateNotifier?.value = right(error),
);
break;

View File

@ -1,109 +0,0 @@
part of 'cell_service.dart';
typedef GridCellMap = LinkedHashMap<String, GridCell>;
class _GridCellCacheObject {
_GridCellCacheKey key;
dynamic object;
_GridCellCacheObject({
required this.key,
required this.object,
});
}
class _GridCellCacheKey {
final String fieldId;
final String rowId;
_GridCellCacheKey({
required this.fieldId,
required this.rowId,
});
}
abstract class GridCellCacheDelegate {
void onFieldUpdated(void Function(Field) callback);
}
class GridCellCacheService {
final String gridId;
final GridCellCacheDelegate delegate;
/// fieldId: {objectId: callback}
final Map<String, Map<String, List<VoidCallback>>> _fieldListenerByFieldId = {};
/// fieldId: {cacheKey: cacheData}
final Map<String, Map<String, dynamic>> _cellDataByFieldId = {};
GridCellCacheService({
required this.gridId,
required this.delegate,
}) {
delegate.onFieldUpdated((field) {
_cellDataByFieldId.remove(field.id);
final map = _fieldListenerByFieldId[field.id];
if (map != null) {
for (final callbacks in map.values) {
for (final callback in callbacks) {
callback();
}
}
}
});
}
void addFieldListener(_GridCellCacheKey cacheKey, VoidCallback onFieldChanged) {
var map = _fieldListenerByFieldId[cacheKey.fieldId];
if (map == null) {
_fieldListenerByFieldId[cacheKey.fieldId] = {};
map = _fieldListenerByFieldId[cacheKey.fieldId];
map![cacheKey.rowId] = [onFieldChanged];
} else {
var objects = map[cacheKey.rowId];
if (objects == null) {
map[cacheKey.rowId] = [onFieldChanged];
} else {
objects.add(onFieldChanged);
}
}
}
void removeFieldListener(_GridCellCacheKey cacheKey, VoidCallback fn) {
var callbacks = _fieldListenerByFieldId[cacheKey.fieldId]?[cacheKey.rowId];
final index = callbacks?.indexWhere((callback) => callback == fn);
if (index != null && index != -1) {
callbacks?.removeAt(index);
}
}
void insert<T extends _GridCellCacheObject>(T item) {
var map = _cellDataByFieldId[item.key.fieldId];
if (map == null) {
_cellDataByFieldId[item.key.fieldId] = {};
map = _cellDataByFieldId[item.key.fieldId];
}
map![item.key.rowId] = item.object;
}
T? get<T>(_GridCellCacheKey key) {
final map = _cellDataByFieldId[key.fieldId];
if (map == null) {
return null;
} else {
final object = map[key.rowId];
if (object is T) {
return object;
} else {
if (object != null) {
Log.error("Cache data type does not match the cache data type");
}
return null;
}
}
}
Future<void> dispose() async {
_fieldListenerByFieldId.clear();
_cellDataByFieldId.clear();
}
}

View File

@ -0,0 +1,70 @@
part of 'cell_service.dart';
typedef GridCellMap = LinkedHashMap<String, GridCellIdentifier>;
class GridCell {
dynamic object;
GridCell({
required this.object,
});
}
/// Use to index the cell in the grid.
/// We use [fieldId + rowId] to identify the cell.
class GridCellCacheKey {
final String fieldId;
final String rowId;
GridCellCacheKey({
required this.fieldId,
required this.rowId,
});
}
/// GridCellCache is used to cache cell data of each block.
/// We use GridCellCacheKey to index the cell in the cache.
/// Read https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/frontend/grid
/// for more information
class GridCellCache {
final String gridId;
/// fieldId: {cacheKey: GridCell}
final Map<String, Map<String, dynamic>> _cellDataByFieldId = {};
GridCellCache({
required this.gridId,
});
void remove(String fieldId) {
_cellDataByFieldId.remove(fieldId);
}
void insert<T extends GridCell>(GridCellCacheKey key, T value) {
var map = _cellDataByFieldId[key.fieldId];
if (map == null) {
_cellDataByFieldId[key.fieldId] = {};
map = _cellDataByFieldId[key.fieldId];
}
map![key.rowId] = value.object;
}
T? get<T>(GridCellCacheKey key) {
final map = _cellDataByFieldId[key.fieldId];
if (map == null) {
return null;
} else {
final value = map[key.rowId];
if (value is T) {
return value;
} else {
if (value != null) {
Log.error("Expected value type: $T, but receive $value");
}
return null;
}
}
}
Future<void> dispose() async {
_cellDataByFieldId.clear();
}
}

View File

@ -3,60 +3,28 @@ part of 'cell_service.dart';
abstract class IGridCellDataConfig {
// The cell data will reload if it receives the field's change notification.
bool get reloadOnFieldChanged;
// When the reloadOnCellChanged is true, it will load the cell data after user input.
// For example: The number cell reload the cell data that carries the format
// user input: 12
// cell display: $12
bool get reloadOnCellChanged;
}
class GridCellDataConfig implements IGridCellDataConfig {
@override
final bool reloadOnCellChanged;
@override
final bool reloadOnFieldChanged;
const GridCellDataConfig({
this.reloadOnCellChanged = false,
this.reloadOnFieldChanged = false,
});
}
abstract class IGridCellDataLoader<T> {
Future<T?> loadData();
IGridCellDataConfig get config;
}
abstract class ICellDataParser<T> {
abstract class IGridCellDataParser<T> {
T? parserData(List<int> data);
}
class GridCellDataLoader<T> extends IGridCellDataLoader<T> {
class GridCellDataLoader<T> {
final CellService service = CellService();
final GridCell gridCell;
final ICellDataParser<T> parser;
@override
final IGridCellDataConfig config;
final GridCellIdentifier cellId;
final IGridCellDataParser<T> parser;
final bool reloadOnFieldChanged;
GridCellDataLoader({
required this.gridCell,
required this.cellId,
required this.parser,
this.config = const GridCellDataConfig(),
this.reloadOnFieldChanged = false,
});
@override
Future<T?> loadData() {
final fut = service.getCell(
gridId: gridCell.gridId,
fieldId: gridCell.field.id,
rowId: gridCell.rowId,
);
final fut = service.getCell(cellId: cellId);
return fut.then(
(result) => result.fold((Cell cell) {
(result) => result.fold((GridCellPB cell) {
try {
return parser.parserData(cell.data);
} catch (e, s) {
@ -72,30 +40,7 @@ class GridCellDataLoader<T> extends IGridCellDataLoader<T> {
}
}
class SelectOptionCellDataLoader extends IGridCellDataLoader<SelectOptionCellData> {
final SelectOptionService service;
final GridCell gridCell;
SelectOptionCellDataLoader({
required this.gridCell,
}) : service = SelectOptionService(gridCell: gridCell);
@override
Future<SelectOptionCellData?> loadData() async {
return service.getOpitonContext().then((result) {
return result.fold(
(data) => data,
(err) {
Log.error(err);
return null;
},
);
});
}
@override
IGridCellDataConfig get config => const GridCellDataConfig(reloadOnFieldChanged: true);
}
class StringCellDataParser implements ICellDataParser<String> {
class StringCellDataParser implements IGridCellDataParser<String> {
@override
String? parserData(List<int> data) {
final s = utf8.decode(data);
@ -103,32 +48,32 @@ class StringCellDataParser implements ICellDataParser<String> {
}
}
class DateCellDataParser implements ICellDataParser<DateCellData> {
class DateCellDataParser implements IGridCellDataParser<DateCellDataPB> {
@override
DateCellData? parserData(List<int> data) {
DateCellDataPB? parserData(List<int> data) {
if (data.isEmpty) {
return null;
}
return DateCellData.fromBuffer(data);
return DateCellDataPB.fromBuffer(data);
}
}
class SelectOptionCellDataParser implements ICellDataParser<SelectOptionCellData> {
class SelectOptionCellDataParser implements IGridCellDataParser<SelectOptionCellDataPB> {
@override
SelectOptionCellData? parserData(List<int> data) {
SelectOptionCellDataPB? parserData(List<int> data) {
if (data.isEmpty) {
return null;
}
return SelectOptionCellData.fromBuffer(data);
return SelectOptionCellDataPB.fromBuffer(data);
}
}
class URLCellDataParser implements ICellDataParser<URLCellData> {
class URLCellDataParser implements IGridCellDataParser<URLCellDataPB> {
@override
URLCellData? parserData(List<int> data) {
URLCellDataPB? parserData(List<int> data) {
if (data.isEmpty) {
return null;
}
return URLCellData.fromBuffer(data);
return URLCellDataPB.fromBuffer(data);
}
}

View File

@ -1,25 +1,22 @@
part of 'cell_service.dart';
abstract class _GridCellDataPersistence<D> {
/// Save the cell data to disk
/// You can extend this class to do custom operations. For example, the DateCellDataPersistence.
abstract class IGridCellDataPersistence<D> {
Future<Option<FlowyError>> save(D data);
}
class CellDataPersistence implements _GridCellDataPersistence<String> {
final GridCell gridCell;
class CellDataPersistence implements IGridCellDataPersistence<String> {
final GridCellIdentifier cellId;
CellDataPersistence({
required this.gridCell,
required this.cellId,
});
final CellService _cellService = CellService();
@override
Future<Option<FlowyError>> save(String data) async {
final fut = _cellService.updateCell(
gridId: gridCell.gridId,
fieldId: gridCell.field.id,
rowId: gridCell.rowId,
data: data,
);
final fut = _cellService.updateCell(cellId: cellId, data: data);
return fut.then((result) {
return result.fold(
@ -35,15 +32,15 @@ class CalendarData with _$CalendarData {
const factory CalendarData({required DateTime date, String? time}) = _CalendarData;
}
class DateCellDataPersistence implements _GridCellDataPersistence<CalendarData> {
final GridCell gridCell;
class DateCellDataPersistence implements IGridCellDataPersistence<CalendarData> {
final GridCellIdentifier cellId;
DateCellDataPersistence({
required this.gridCell,
required this.cellId,
});
@override
Future<Option<FlowyError>> save(CalendarData data) {
var payload = DateChangesetPayload.create()..cellIdentifier = _cellIdentifier(gridCell);
var payload = DateChangesetPayloadPB.create()..cellIdentifier = _makeCellIdPayload(cellId);
final date = (data.date.millisecondsSinceEpoch ~/ 1000).toString();
payload.date = date;
@ -61,9 +58,9 @@ class DateCellDataPersistence implements _GridCellDataPersistence<CalendarData>
}
}
CellIdentifierPayload _cellIdentifier(GridCell gridCell) {
return CellIdentifierPayload.create()
..gridId = gridCell.gridId
..fieldId = gridCell.field.id
..rowId = gridCell.rowId;
GridCellIdentifierPayloadPB _makeCellIdPayload(GridCellIdentifier cellId) {
return GridCellIdentifierPayloadPB.create()
..gridId = cellId.gridId
..fieldId = cellId.fieldId
..rowId = cellId.rowId;
}

View File

@ -0,0 +1,60 @@
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flutter/foundation.dart';
import 'cell_service.dart';
abstract class GridFieldChangedNotifier {
void onFieldChanged(void Function(GridFieldPB) callback);
void dispose();
}
/// GridPB's cell helper wrapper that enables each cell will get notified when the corresponding field was changed.
/// You Register an onFieldChanged callback to listen to the cell changes, and unregister if you don't want to listen.
class GridCellFieldNotifier {
/// fieldId: {objectId: callback}
final Map<String, Map<String, List<VoidCallback>>> _fieldListenerByFieldId = {};
GridCellFieldNotifier({required GridFieldChangedNotifier notifier}) {
notifier.onFieldChanged(
(field) {
final map = _fieldListenerByFieldId[field.id];
if (map != null) {
for (final callbacks in map.values) {
for (final callback in callbacks) {
callback();
}
}
}
},
);
}
///
void register(GridCellCacheKey cacheKey, VoidCallback onFieldChanged) {
var map = _fieldListenerByFieldId[cacheKey.fieldId];
if (map == null) {
_fieldListenerByFieldId[cacheKey.fieldId] = {};
map = _fieldListenerByFieldId[cacheKey.fieldId];
map![cacheKey.rowId] = [onFieldChanged];
} else {
var objects = map[cacheKey.rowId];
if (objects == null) {
map[cacheKey.rowId] = [onFieldChanged];
} else {
objects.add(onFieldChanged);
}
}
}
void unregister(GridCellCacheKey cacheKey, VoidCallback fn) {
var callbacks = _fieldListenerByFieldId[cacheKey.fieldId]?[cacheKey.rowId];
final index = callbacks?.indexWhere((callback) => callback == fn);
if (index != null && index != -1) {
callbacks?.removeAt(index);
}
}
Future<void> dispose() async {
_fieldListenerByFieldId.clear();
}
}

View File

@ -1,26 +1,29 @@
import 'dart:async';
import 'dart:collection';
import 'package:app_flowy/workspace/application/grid/grid_service.dart';
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option_entities.pb.dart';
import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';
import 'package:app_flowy/workspace/application/grid/cell/select_option_service.dart';
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
import 'dart:convert' show utf8;
import '../../field/type_option/type_option_service.dart';
import 'cell_field_notifier.dart';
part 'cell_service.freezed.dart';
part 'cell_data_loader.dart';
part 'context_builder.dart';
part 'cache.dart';
part 'cell_cache.dart';
part 'cell_data_persistence.dart';
// key: rowId
@ -29,44 +32,46 @@ class CellService {
CellService();
Future<Either<void, FlowyError>> updateCell({
required String gridId,
required String fieldId,
required String rowId,
required GridCellIdentifier cellId,
required String data,
}) {
final payload = CellChangeset.create()
..gridId = gridId
..fieldId = fieldId
..rowId = rowId
..cellContentChangeset = data;
final payload = CellChangesetPB.create()
..gridId = cellId.gridId
..fieldId = cellId.fieldId
..rowId = cellId.rowId
..content = data;
return GridEventUpdateCell(payload).send();
}
Future<Either<Cell, FlowyError>> getCell({
required String gridId,
required String fieldId,
required String rowId,
Future<Either<GridCellPB, FlowyError>> getCell({
required GridCellIdentifier cellId,
}) {
final payload = CellIdentifierPayload.create()
..gridId = gridId
..fieldId = fieldId
..rowId = rowId;
final payload = GridCellIdentifierPayloadPB.create()
..gridId = cellId.gridId
..fieldId = cellId.fieldId
..rowId = cellId.rowId;
return GridEventGetCell(payload).send();
}
}
/// Id of the cell
/// We can locate the cell by using gridId + rowId + field.id.
@freezed
class GridCell with _$GridCell {
const factory GridCell({
class GridCellIdentifier with _$GridCellIdentifier {
const factory GridCellIdentifier({
required String gridId,
required String rowId,
required Field field,
}) = _GridCell;
required GridFieldPB field,
}) = _GridCellIdentifier;
// ignore: unused_element
const GridCell._();
const GridCellIdentifier._();
String cellId() {
return rowId + field.id + "${field.fieldType}";
String get fieldId => field.id;
FieldType get fieldType => field.fieldType;
ValueKey key() {
return ValueKey(rowId + fieldId + "${field.fieldType}");
}
}

View File

@ -1,154 +1,183 @@
part of 'cell_service.dart';
typedef GridCellContext = _GridCellContext<String, String>;
typedef GridSelectOptionCellContext = _GridCellContext<SelectOptionCellData, String>;
typedef GridDateCellContext = _GridCellContext<DateCellData, CalendarData>;
typedef GridURLCellContext = _GridCellContext<URLCellData, String>;
typedef GridCellController = IGridCellController<String, String>;
typedef GridSelectOptionCellController = IGridCellController<SelectOptionCellDataPB, String>;
typedef GridDateCellController = IGridCellController<DateCellDataPB, CalendarData>;
typedef GridURLCellController = IGridCellController<URLCellDataPB, String>;
class GridCellContextBuilder {
final GridCellCacheService _cellCache;
final GridCell _gridCell;
GridCellContextBuilder({
required GridCellCacheService cellCache,
required GridCell gridCell,
class GridCellControllerBuilder {
final GridCellIdentifier _cellId;
final GridCellCache _cellCache;
final GridFieldCache _fieldCache;
GridCellControllerBuilder({
required GridCellIdentifier cellId,
required GridCellCache cellCache,
required GridFieldCache fieldCache,
}) : _cellCache = cellCache,
_gridCell = gridCell;
_fieldCache = fieldCache,
_cellId = cellId;
_GridCellContext build() {
switch (_gridCell.field.fieldType) {
IGridCellController build() {
final cellFieldNotifier = GridCellFieldNotifier(notifier: _GridFieldChangedNotifierImpl(_fieldCache));
switch (_cellId.fieldType) {
case FieldType.Checkbox:
final cellDataLoader = GridCellDataLoader(
gridCell: _gridCell,
cellId: _cellId,
parser: StringCellDataParser(),
);
return GridCellContext(
gridCell: _gridCell,
return GridCellController(
cellId: _cellId,
cellCache: _cellCache,
cellDataLoader: cellDataLoader,
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
fieldNotifier: cellFieldNotifier,
cellDataPersistence: CellDataPersistence(cellId: _cellId),
);
case FieldType.DateTime:
final cellDataLoader = GridCellDataLoader(
gridCell: _gridCell,
cellId: _cellId,
parser: DateCellDataParser(),
config: const GridCellDataConfig(reloadOnFieldChanged: true),
reloadOnFieldChanged: true,
);
return GridDateCellContext(
gridCell: _gridCell,
return GridDateCellController(
cellId: _cellId,
cellCache: _cellCache,
cellDataLoader: cellDataLoader,
cellDataPersistence: DateCellDataPersistence(gridCell: _gridCell),
fieldNotifier: cellFieldNotifier,
cellDataPersistence: DateCellDataPersistence(cellId: _cellId),
);
case FieldType.Number:
final cellDataLoader = GridCellDataLoader(
gridCell: _gridCell,
cellId: _cellId,
parser: StringCellDataParser(),
config: const GridCellDataConfig(reloadOnCellChanged: true, reloadOnFieldChanged: true),
reloadOnFieldChanged: true,
);
return GridCellContext(
gridCell: _gridCell,
return GridCellController(
cellId: _cellId,
cellCache: _cellCache,
cellDataLoader: cellDataLoader,
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
fieldNotifier: cellFieldNotifier,
cellDataPersistence: CellDataPersistence(cellId: _cellId),
);
case FieldType.RichText:
final cellDataLoader = GridCellDataLoader(
gridCell: _gridCell,
cellId: _cellId,
parser: StringCellDataParser(),
);
return GridCellContext(
gridCell: _gridCell,
return GridCellController(
cellId: _cellId,
cellCache: _cellCache,
cellDataLoader: cellDataLoader,
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
fieldNotifier: cellFieldNotifier,
cellDataPersistence: CellDataPersistence(cellId: _cellId),
);
case FieldType.MultiSelect:
case FieldType.SingleSelect:
final cellDataLoader = GridCellDataLoader(
gridCell: _gridCell,
cellId: _cellId,
parser: SelectOptionCellDataParser(),
config: const GridCellDataConfig(reloadOnFieldChanged: true),
reloadOnFieldChanged: true,
);
return GridSelectOptionCellContext(
gridCell: _gridCell,
return GridSelectOptionCellController(
cellId: _cellId,
cellCache: _cellCache,
cellDataLoader: cellDataLoader,
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
fieldNotifier: cellFieldNotifier,
cellDataPersistence: CellDataPersistence(cellId: _cellId),
);
case FieldType.URL:
final cellDataLoader = GridCellDataLoader(
gridCell: _gridCell,
cellId: _cellId,
parser: URLCellDataParser(),
);
return GridURLCellContext(
gridCell: _gridCell,
return GridURLCellController(
cellId: _cellId,
cellCache: _cellCache,
cellDataLoader: cellDataLoader,
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
fieldNotifier: cellFieldNotifier,
cellDataPersistence: CellDataPersistence(cellId: _cellId),
);
}
throw UnimplementedError;
}
}
// T: the type of the CellData
// D: the type of the data that will be save to disk
/// IGridCellController is used to manipulate the cell and receive notifications.
/// * Read/Write cell data
/// * Listen on field/cell notifications.
///
/// Generic T represents the type of the cell data.
/// Generic D represents the type of data that will be saved to the disk
///
// ignore: must_be_immutable
class _GridCellContext<T, D> extends Equatable {
final GridCell gridCell;
final GridCellCacheService cellCache;
final _GridCellCacheKey _cacheKey;
final IGridCellDataLoader<T> cellDataLoader;
final _GridCellDataPersistence<D> cellDataPersistence;
class IGridCellController<T, D> extends Equatable {
final GridCellIdentifier cellId;
final GridCellCache _cellsCache;
final GridCellCacheKey _cacheKey;
final FieldService _fieldService;
final GridCellFieldNotifier _fieldNotifier;
final GridCellDataLoader<T> _cellDataLoader;
final IGridCellDataPersistence<D> _cellDataPersistence;
late final CellListener _cellListener;
late final ValueNotifier<T?>? _cellDataNotifier;
ValueNotifier<T?>? _cellDataNotifier;
bool isListening = false;
VoidCallback? _onFieldChangedFn;
Timer? _loadDataOperation;
Timer? _saveDataOperation;
bool _isDispose = false;
_GridCellContext({
required this.gridCell,
required this.cellCache,
required this.cellDataLoader,
required this.cellDataPersistence,
}) : _fieldService = FieldService(gridId: gridCell.gridId, fieldId: gridCell.field.id),
_cacheKey = _GridCellCacheKey(rowId: gridCell.rowId, fieldId: gridCell.field.id);
IGridCellController({
required this.cellId,
required GridCellCache cellCache,
required GridCellFieldNotifier fieldNotifier,
required GridCellDataLoader<T> cellDataLoader,
required IGridCellDataPersistence<D> cellDataPersistence,
}) : _cellsCache = cellCache,
_cellDataLoader = cellDataLoader,
_cellDataPersistence = cellDataPersistence,
_fieldNotifier = fieldNotifier,
_fieldService = FieldService(gridId: cellId.gridId, fieldId: cellId.field.id),
_cacheKey = GridCellCacheKey(rowId: cellId.rowId, fieldId: cellId.field.id);
_GridCellContext<T, D> clone() {
return _GridCellContext(
gridCell: gridCell,
cellDataLoader: cellDataLoader,
cellCache: cellCache,
cellDataPersistence: cellDataPersistence);
IGridCellController<T, D> clone() {
return IGridCellController(
cellId: cellId,
cellDataLoader: _cellDataLoader,
cellCache: _cellsCache,
fieldNotifier: _fieldNotifier,
cellDataPersistence: _cellDataPersistence);
}
String get gridId => gridCell.gridId;
String get gridId => cellId.gridId;
String get rowId => gridCell.rowId;
String get rowId => cellId.rowId;
String get cellId => gridCell.rowId + gridCell.field.id;
String get fieldId => cellId.field.id;
String get fieldId => gridCell.field.id;
GridFieldPB get field => cellId.field;
Field get field => gridCell.field;
FieldType get fieldType => cellId.field.fieldType;
FieldType get fieldType => gridCell.field.fieldType;
VoidCallback? startListening({required void Function(T?) onCellChanged}) {
VoidCallback? startListening({required void Function(T?) onCellChanged, VoidCallback? onCellFieldChanged}) {
if (isListening) {
Log.error("Already started. It seems like you should call clone first");
return null;
}
isListening = true;
_cellDataNotifier = ValueNotifier(cellCache.get(_cacheKey));
_cellListener = CellListener(rowId: gridCell.rowId, fieldId: gridCell.field.id);
_cellDataNotifier = ValueNotifier(_cellsCache.get(_cacheKey));
_cellListener = CellListener(rowId: cellId.rowId, fieldId: cellId.field.id);
/// 1.Listen on user edit event and load the new cell data if needed.
/// For example:
/// user input: 12
/// cell display: $12
_cellListener.start(onCellChanged: (result) {
result.fold(
(_) => _loadData(),
@ -156,22 +185,27 @@ class _GridCellContext<T, D> extends Equatable {
);
});
if (cellDataLoader.config.reloadOnFieldChanged) {
_onFieldChangedFn = () {
_loadData();
};
cellCache.addFieldListener(_cacheKey, _onFieldChangedFn!);
}
/// 2.Listen on the field event and load the cell data if needed.
_onFieldChangedFn = () {
if (onCellFieldChanged != null) {
onCellFieldChanged();
}
onCellChangedFn() {
onCellChanged(_cellDataNotifier?.value);
if (cellDataLoader.config.reloadOnCellChanged) {
/// reloadOnFieldChanged should be true if you need to load the data when the corresponding field is changed
/// For example:
/// 12 -> $12
if (_cellDataLoader.reloadOnFieldChanged) {
_loadData();
}
}
};
_fieldNotifier.register(_cacheKey, _onFieldChangedFn!);
/// Notify the listener, the cell data was changed.
onCellChangedFn() => onCellChanged(_cellDataNotifier?.value);
_cellDataNotifier?.addListener(onCellChangedFn);
// Return the function pointer that can be used when calling removeListener.
return onCellChangedFn;
}
@ -179,29 +213,45 @@ class _GridCellContext<T, D> extends Equatable {
_cellDataNotifier?.removeListener(fn);
}
T? getCellData({bool loadIfNoCache = true}) {
final data = cellCache.get(_cacheKey);
if (data == null && loadIfNoCache) {
/// Return the cell data.
/// The cell data will be read from the Cache first, and load from disk if it does not exist.
/// You can set [loadIfNotExist] to false (default is true) to disable loading the cell data.
T? getCellData({bool loadIfNotExist = true}) {
final data = _cellsCache.get(_cacheKey);
if (data == null && loadIfNotExist) {
_loadData();
}
return data;
}
Future<Either<FieldTypeOptionData, FlowyError>> getTypeOptionData() {
return _fieldService.getFieldTypeOptionData(fieldType: fieldType);
/// Return the FieldTypeOptionDataPB that can be parsed into corresponding class using the [parser].
/// [PD] is the type that the parser return.
Future<Either<PD, FlowyError>> getFieldTypeOption<PD, P extends TypeOptionDataParser>(P parser) {
return _fieldService.getFieldTypeOptionData(fieldType: fieldType).then((result) {
return result.fold(
(data) => parser.fromBuffer(data.typeOptionData),
(err) => right(err),
);
});
}
/// Save the cell data to disk
/// You can set [dedeplicate] to true (default is false) to reduce the save operation.
/// It's useful when you call this method when user editing the [TextField].
/// The default debounce interval is 300 milliseconds.
void saveCellData(D data, {bool deduplicate = false, void Function(Option<FlowyError>)? resultCallback}) async {
if (deduplicate) {
_loadDataOperation?.cancel();
_loadDataOperation = Timer(const Duration(milliseconds: 300), () async {
final result = await cellDataPersistence.save(data);
_saveDataOperation?.cancel();
_saveDataOperation = Timer(const Duration(milliseconds: 300), () async {
final result = await _cellDataPersistence.save(data);
if (resultCallback != null) {
resultCallback(result);
}
});
} else {
final result = await cellDataPersistence.save(data);
final result = await _cellDataPersistence.save(data);
if (resultCallback != null) {
resultCallback(result);
}
@ -209,26 +259,59 @@ class _GridCellContext<T, D> extends Equatable {
}
void _loadData() {
_saveDataOperation?.cancel();
_loadDataOperation?.cancel();
_loadDataOperation = Timer(const Duration(milliseconds: 10), () {
cellDataLoader.loadData().then((data) {
_cellDataLoader.loadData().then((data) {
_cellDataNotifier?.value = data;
cellCache.insert(_GridCellCacheObject(key: _cacheKey, object: data));
_cellsCache.insert(_cacheKey, GridCell(object: data));
});
});
}
void dispose() {
if (_isDispose) {
Log.error("$this should only dispose once");
return;
}
_isDispose = true;
_cellListener.stop();
_loadDataOperation?.cancel();
_saveDataOperation?.cancel();
_cellDataNotifier = null;
if (_onFieldChangedFn != null) {
cellCache.removeFieldListener(_cacheKey, _onFieldChangedFn!);
_fieldNotifier.unregister(_cacheKey, _onFieldChangedFn!);
_onFieldChangedFn = null;
}
}
@override
List<Object> get props => [cellCache.get(_cacheKey) ?? "", cellId];
List<Object> get props => [_cellsCache.get(_cacheKey) ?? "", cellId.rowId + cellId.field.id];
}
class _GridFieldChangedNotifierImpl extends GridFieldChangedNotifier {
final GridFieldCache _cache;
FieldChangesetCallback? _onChangesetFn;
_GridFieldChangedNotifierImpl(GridFieldCache cache) : _cache = cache;
@override
void dispose() {
if (_onChangesetFn != null) {
_cache.removeListener(onChangsetListener: _onChangesetFn!);
_onChangesetFn = null;
}
}
@override
void onFieldChanged(void Function(GridFieldPB p1) callback) {
_onChangesetFn = (GridFieldChangesetPB changeset) {
for (final updatedField in changeset.updatedFields) {
callback(updatedField);
}
};
_cache.addListener(onChangeset: _onChangesetFn);
}
}

View File

@ -6,7 +6,7 @@ import 'cell_service/cell_service.dart';
part 'checkbox_cell_bloc.freezed.dart';
class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
final GridCellContext cellContext;
final GridCellController cellContext;
void Function()? _onCellChangedFn;
CheckboxCellBloc({
@ -67,7 +67,7 @@ class CheckboxCellState with _$CheckboxCellState {
required bool isSelected,
}) = _CheckboxCellState;
factory CheckboxCellState.initial(GridCellContext context) {
factory CheckboxCellState.initial(GridCellController context) {
return CheckboxCellState(isSelected: _isSelected(context.getCellData()));
}
}

View File

@ -5,6 +5,7 @@ import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:table_calendar/table_calendar.dart';
@ -16,12 +17,12 @@ import 'package:fixnum/fixnum.dart' as $fixnum;
part 'date_cal_bloc.freezed.dart';
class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
final GridDateCellContext cellContext;
final GridDateCellController cellContext;
void Function()? _onCellChangedFn;
DateCalBloc({
required DateTypeOption dateTypeOption,
required DateCellData? cellData,
required DateCellDataPB? cellData,
required this.cellContext,
}) : super(DateCalState.initial(dateTypeOption, cellData)) {
on<DateCalEvent>(
@ -37,7 +38,7 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
setFocusedDay: (focusedDay) {
emit(state.copyWith(focusedDay: focusedDay));
},
didReceiveCellUpdate: (DateCellData? cellData) {
didReceiveCellUpdate: (DateCellDataPB? cellData) {
final calData = calDataFromCellData(cellData);
final time = calData.foldRight("", (dateData, previous) => dateData.time);
emit(state.copyWith(calData: calData, time: time));
@ -187,7 +188,7 @@ class DateCalEvent with _$DateCalEvent {
const factory DateCalEvent.setDateFormat(DateFormat dateFormat) = _DateFormat;
const factory DateCalEvent.setIncludeTime(bool includeTime) = _IncludeTime;
const factory DateCalEvent.setTime(String time) = _Time;
const factory DateCalEvent.didReceiveCellUpdate(DateCellData? data) = _DidReceiveCellUpdate;
const factory DateCalEvent.didReceiveCellUpdate(DateCellDataPB? data) = _DidReceiveCellUpdate;
const factory DateCalEvent.didUpdateCalData(Option<CalendarData> data, Option<String> timeFormatError) =
_DidUpdateCalData;
}
@ -206,7 +207,7 @@ class DateCalState with _$DateCalState {
factory DateCalState.initial(
DateTypeOption dateTypeOption,
DateCellData? cellData,
DateCellDataPB? cellData,
) {
Option<CalendarData> calData = calDataFromCellData(cellData);
final time = calData.foldRight("", (dateData, previous) => dateData.time);
@ -232,7 +233,7 @@ String _timeHintText(DateTypeOption typeOption) {
return "";
}
Option<CalendarData> calDataFromCellData(DateCellData? cellData) {
Option<CalendarData> calDataFromCellData(DateCellDataPB? cellData) {
String? time = timeFromCellData(cellData);
Option<CalendarData> calData = none();
if (cellData != null) {
@ -248,7 +249,7 @@ $fixnum.Int64 timestampFromDateTime(DateTime dateTime) {
return $fixnum.Int64(timestamp);
}
String? timeFromCellData(DateCellData? cellData) {
String? timeFromCellData(DateCellDataPB? cellData) {
String? time;
if (cellData?.hasTime() ?? false) {
time = cellData?.time;

View File

@ -1,4 +1,4 @@
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@ -7,7 +7,7 @@ import 'cell_service/cell_service.dart';
part 'date_cell_bloc.freezed.dart';
class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
final GridDateCellContext cellContext;
final GridDateCellController cellContext;
void Function()? _onCellChangedFn;
DateCellBloc({required this.cellContext}) : super(DateCellState.initial(cellContext)) {
@ -15,10 +15,10 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
(event, emit) async {
event.when(
initial: () => _startListening(),
didReceiveCellUpdate: (DateCellData? cellData) {
didReceiveCellUpdate: (DateCellDataPB? cellData) {
emit(state.copyWith(data: cellData, dateStr: _dateStrFromCellData(cellData)));
},
didReceiveFieldUpdate: (Field value) => emit(state.copyWith(field: value)),
didReceiveFieldUpdate: (GridFieldPB value) => emit(state.copyWith(field: value)),
);
},
);
@ -48,19 +48,19 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
@freezed
class DateCellEvent with _$DateCellEvent {
const factory DateCellEvent.initial() = _InitialCell;
const factory DateCellEvent.didReceiveCellUpdate(DateCellData? data) = _DidReceiveCellUpdate;
const factory DateCellEvent.didReceiveFieldUpdate(Field field) = _DidReceiveFieldUpdate;
const factory DateCellEvent.didReceiveCellUpdate(DateCellDataPB? data) = _DidReceiveCellUpdate;
const factory DateCellEvent.didReceiveFieldUpdate(GridFieldPB field) = _DidReceiveFieldUpdate;
}
@freezed
class DateCellState with _$DateCellState {
const factory DateCellState({
required DateCellData? data,
required DateCellDataPB? data,
required String dateStr,
required Field field,
required GridFieldPB field,
}) = _DateCellState;
factory DateCellState.initial(GridDateCellContext context) {
factory DateCellState.initial(GridDateCellController context) {
final cellData = context.getCellData();
return DateCellState(
@ -71,7 +71,7 @@ class DateCellState with _$DateCellState {
}
}
String _dateStrFromCellData(DateCellData? cellData) {
String _dateStrFromCellData(DateCellDataPB? cellData) {
String dateStr = "";
if (cellData != null) {
dateStr = cellData.date + " " + cellData.time;

View File

@ -8,7 +8,7 @@ import 'cell_service/cell_service.dart';
part 'number_cell_bloc.freezed.dart';
class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
final GridCellContext cellContext;
final GridCellController cellContext;
void Function()? _onCellChangedFn;
NumberCellBloc({
@ -72,7 +72,7 @@ class NumberCellState with _$NumberCellState {
required Either<String, FlowyError> content,
}) = _NumberCellState;
factory NumberCellState.initial(GridCellContext context) {
factory NumberCellState.initial(GridCellController context) {
final cellContent = context.getCellData() ?? "";
return NumberCellState(
content: left(cellContent),

View File

@ -1,5 +1,5 @@
import 'dart:async';
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
@ -7,7 +7,7 @@ import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_serv
part 'select_option_cell_bloc.freezed.dart';
class SelectOptionCellBloc extends Bloc<SelectOptionCellEvent, SelectOptionCellState> {
final GridSelectOptionCellContext cellContext;
final GridSelectOptionCellController cellContext;
void Function()? _onCellChangedFn;
SelectOptionCellBloc({
@ -56,17 +56,17 @@ class SelectOptionCellBloc extends Bloc<SelectOptionCellEvent, SelectOptionCellS
class SelectOptionCellEvent with _$SelectOptionCellEvent {
const factory SelectOptionCellEvent.initial() = _InitialCell;
const factory SelectOptionCellEvent.didReceiveOptions(
List<SelectOption> selectedOptions,
List<SelectOptionPB> selectedOptions,
) = _DidReceiveOptions;
}
@freezed
class SelectOptionCellState with _$SelectOptionCellState {
const factory SelectOptionCellState({
required List<SelectOption> selectedOptions,
required List<SelectOptionPB> selectedOptions,
}) = _SelectOptionCellState;
factory SelectOptionCellState.initial(GridSelectOptionCellContext context) {
factory SelectOptionCellState.initial(GridSelectOptionCellController context) {
final data = context.getCellData();
return SelectOptionCellState(

View File

@ -1,8 +1,7 @@
import 'dart:async';
import 'package:app_flowy/workspace/application/grid/field/grid_listenr.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
@ -13,16 +12,13 @@ part 'select_option_editor_bloc.freezed.dart';
class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOptionEditorState> {
final SelectOptionService _selectOptionService;
final GridSelectOptionCellContext cellContext;
late final GridFieldsListener _fieldListener;
void Function()? _onCellChangedFn;
final GridSelectOptionCellController cellController;
Timer? _delayOperation;
SelectOptionCellEditorBloc({
required this.cellContext,
}) : _selectOptionService = SelectOptionService(gridCell: cellContext.gridCell),
_fieldListener = GridFieldsListener(gridId: cellContext.gridId),
super(SelectOptionEditorState.initial(cellContext)) {
required this.cellController,
}) : _selectOptionService = SelectOptionService(cellId: cellController.cellId),
super(SelectOptionEditorState.initial(cellController)) {
on<SelectOptionEditorEvent>(
(event, emit) async {
await event.map(
@ -64,13 +60,8 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
@override
Future<void> close() async {
if (_onCellChangedFn != null) {
cellContext.removeListener(_onCellChangedFn!);
_onCellChangedFn = null;
}
_delayOperation?.cancel();
await _fieldListener.stop();
cellContext.dispose();
cellController.dispose();
return super.close();
}
@ -79,7 +70,7 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
result.fold((l) => {}, (err) => Log.error(err));
}
void _deleteOption(SelectOption option) async {
void _deleteOption(SelectOptionPB option) async {
final result = await _selectOptionService.delete(
option: option,
);
@ -87,7 +78,7 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
result.fold((l) => null, (err) => Log.error(err));
}
void _updateOption(SelectOption option) async {
void _updateOption(SelectOptionPB option) async {
final result = await _selectOptionService.update(
option: option,
);
@ -131,8 +122,8 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
});
}
_MakeOptionResult _makeOptions(Option<String> filter, List<SelectOption> allOptions) {
final List<SelectOption> options = List.from(allOptions);
_MakeOptionResult _makeOptions(Option<String> filter, List<SelectOptionPB> allOptions) {
final List<SelectOptionPB> options = List.from(allOptions);
Option<String> createOption = filter;
filter.foldRight(null, (filter, previous) {
@ -157,24 +148,16 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
}
void _startListening() {
_onCellChangedFn = cellContext.startListening(
cellController.startListening(
onCellChanged: ((selectOptionContext) {
if (!isClosed) {
_loadOptions();
}
}),
onCellFieldChanged: () {
_loadOptions();
},
);
_fieldListener.start(onFieldsChanged: (result) {
result.fold(
(changeset) {
if (changeset.updatedFields.isNotEmpty) {
_loadOptions();
}
},
(err) => Log.error(err),
);
});
}
}
@ -182,26 +165,26 @@ class SelectOptionCellEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOpt
class SelectOptionEditorEvent with _$SelectOptionEditorEvent {
const factory SelectOptionEditorEvent.initial() = _Initial;
const factory SelectOptionEditorEvent.didReceiveOptions(
List<SelectOption> options, List<SelectOption> selectedOptions) = _DidReceiveOptions;
List<SelectOptionPB> options, List<SelectOptionPB> selectedOptions) = _DidReceiveOptions;
const factory SelectOptionEditorEvent.newOption(String optionName) = _NewOption;
const factory SelectOptionEditorEvent.selectOption(String optionId) = _SelectOption;
const factory SelectOptionEditorEvent.updateOption(SelectOption option) = _UpdateOption;
const factory SelectOptionEditorEvent.deleteOption(SelectOption option) = _DeleteOption;
const factory SelectOptionEditorEvent.updateOption(SelectOptionPB option) = _UpdateOption;
const factory SelectOptionEditorEvent.deleteOption(SelectOptionPB option) = _DeleteOption;
const factory SelectOptionEditorEvent.filterOption(String optionName) = _SelectOptionFilter;
}
@freezed
class SelectOptionEditorState with _$SelectOptionEditorState {
const factory SelectOptionEditorState({
required List<SelectOption> options,
required List<SelectOption> allOptions,
required List<SelectOption> selectedOptions,
required List<SelectOptionPB> options,
required List<SelectOptionPB> allOptions,
required List<SelectOptionPB> selectedOptions,
required Option<String> createOption,
required Option<String> filter,
}) = _SelectOptionEditorState;
factory SelectOptionEditorState.initial(GridSelectOptionCellContext context) {
final data = context.getCellData(loadIfNoCache: false);
factory SelectOptionEditorState.initial(GridSelectOptionCellController context) {
final data = context.getCellData(loadIfNotExist: false);
return SelectOptionEditorState(
options: data?.options ?? [],
allOptions: data?.options ?? [],
@ -213,7 +196,7 @@ class SelectOptionEditorState with _$SelectOptionEditorState {
}
class _MakeOptionResult {
List<SelectOption> options;
List<SelectOptionPB> options;
Option<String> createOption;
_MakeOptionResult({

View File

@ -2,28 +2,28 @@ import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
import 'cell_service/cell_service.dart';
class SelectOptionService {
final GridCell gridCell;
SelectOptionService({required this.gridCell});
final GridCellIdentifier cellId;
SelectOptionService({required this.cellId});
String get gridId => gridCell.gridId;
String get fieldId => gridCell.field.id;
String get rowId => gridCell.rowId;
String get gridId => cellId.gridId;
String get fieldId => cellId.field.id;
String get rowId => cellId.rowId;
Future<Either<Unit, FlowyError>> create({required String name}) {
return TypeOptionService(gridId: gridId, fieldId: fieldId).newOption(name: name).then(
(result) {
return result.fold(
(option) {
final cellIdentifier = CellIdentifierPayload.create()
final cellIdentifier = GridCellIdentifierPayloadPB.create()
..gridId = gridId
..fieldId = fieldId
..rowId = rowId;
final payload = SelectOptionChangesetPayload.create()
final payload = SelectOptionChangesetPayloadPB.create()
..insertOption = option
..cellIdentifier = cellIdentifier;
return GridEventUpdateSelectOption(payload).send();
@ -35,26 +35,26 @@ class SelectOptionService {
}
Future<Either<Unit, FlowyError>> update({
required SelectOption option,
required SelectOptionPB option,
}) {
final payload = SelectOptionChangesetPayload.create()
final payload = SelectOptionChangesetPayloadPB.create()
..updateOption = option
..cellIdentifier = _cellIdentifier();
return GridEventUpdateSelectOption(payload).send();
}
Future<Either<Unit, FlowyError>> delete({
required SelectOption option,
required SelectOptionPB option,
}) {
final payload = SelectOptionChangesetPayload.create()
final payload = SelectOptionChangesetPayloadPB.create()
..deleteOption = option
..cellIdentifier = _cellIdentifier();
return GridEventUpdateSelectOption(payload).send();
}
Future<Either<SelectOptionCellData, FlowyError>> getOpitonContext() {
final payload = CellIdentifierPayload.create()
Future<Either<SelectOptionCellDataPB, FlowyError>> getOpitonContext() {
final payload = GridCellIdentifierPayloadPB.create()
..gridId = gridId
..fieldId = fieldId
..rowId = rowId;
@ -63,21 +63,21 @@ class SelectOptionService {
}
Future<Either<void, FlowyError>> select({required String optionId}) {
final payload = SelectOptionCellChangesetPayload.create()
final payload = SelectOptionCellChangesetPayloadPB.create()
..cellIdentifier = _cellIdentifier()
..insertOptionId = optionId;
return GridEventUpdateSelectOptionCell(payload).send();
}
Future<Either<void, FlowyError>> unSelect({required String optionId}) {
final payload = SelectOptionCellChangesetPayload.create()
final payload = SelectOptionCellChangesetPayloadPB.create()
..cellIdentifier = _cellIdentifier()
..deleteOptionId = optionId;
return GridEventUpdateSelectOptionCell(payload).send();
}
CellIdentifierPayload _cellIdentifier() {
return CellIdentifierPayload.create()
GridCellIdentifierPayloadPB _cellIdentifier() {
return GridCellIdentifierPayloadPB.create()
..gridId = gridId
..fieldId = fieldId
..rowId = rowId;

View File

@ -6,7 +6,7 @@ import 'cell_service/cell_service.dart';
part 'text_cell_bloc.freezed.dart';
class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
final GridCellContext cellContext;
final GridCellController cellContext;
void Function()? _onCellChangedFn;
TextCellBloc({
required this.cellContext,
@ -63,7 +63,7 @@ class TextCellState with _$TextCellState {
required String content,
}) = _TextCellState;
factory TextCellState.initial(GridCellContext context) => TextCellState(
factory TextCellState.initial(GridCellController context) => TextCellState(
content: context.getCellData() ?? "",
);
}

View File

@ -1,4 +1,4 @@
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
@ -7,7 +7,7 @@ import 'cell_service/cell_service.dart';
part 'url_cell_bloc.freezed.dart';
class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
final GridURLCellContext cellContext;
final GridURLCellController cellContext;
void Function()? _onCellChangedFn;
URLCellBloc({
required this.cellContext,
@ -57,7 +57,7 @@ class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
class URLCellEvent with _$URLCellEvent {
const factory URLCellEvent.initial() = _InitialCell;
const factory URLCellEvent.updateURL(String url) = _UpdateURL;
const factory URLCellEvent.didReceiveCellUpdate(URLCellData? cell) = _DidReceiveCellUpdate;
const factory URLCellEvent.didReceiveCellUpdate(URLCellDataPB? cell) = _DidReceiveCellUpdate;
}
@freezed
@ -67,7 +67,7 @@ class URLCellState with _$URLCellState {
required String url,
}) = _URLCellState;
factory URLCellState.initial(GridURLCellContext context) {
factory URLCellState.initial(GridURLCellController context) {
final cellData = context.getCellData();
return URLCellState(
content: cellData?.content ?? "",

View File

@ -1,4 +1,4 @@
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
@ -7,7 +7,7 @@ import 'cell_service/cell_service.dart';
part 'url_cell_editor_bloc.freezed.dart';
class URLCellEditorBloc extends Bloc<URLCellEditorEvent, URLCellEditorState> {
final GridURLCellContext cellContext;
final GridURLCellController cellContext;
void Function()? _onCellChangedFn;
URLCellEditorBloc({
required this.cellContext,
@ -54,7 +54,7 @@ class URLCellEditorBloc extends Bloc<URLCellEditorEvent, URLCellEditorState> {
@freezed
class URLCellEditorEvent with _$URLCellEditorEvent {
const factory URLCellEditorEvent.initial() = _InitialCell;
const factory URLCellEditorEvent.didReceiveCellUpdate(URLCellData? cell) = _DidReceiveCellUpdate;
const factory URLCellEditorEvent.didReceiveCellUpdate(URLCellDataPB? cell) = _DidReceiveCellUpdate;
const factory URLCellEditorEvent.updateText(String text) = _UpdateText;
}
@ -64,7 +64,7 @@ class URLCellEditorState with _$URLCellEditorState {
required String content,
}) = _URLCellEditorState;
factory URLCellEditorState.initial(GridURLCellContext context) {
factory URLCellEditorState.initial(GridURLCellController context) {
final cellData = context.getCellData();
return URLCellEditorState(
content: cellData?.content ?? "",

View File

@ -10,8 +10,8 @@ part 'field_action_sheet_bloc.freezed.dart';
class FieldActionSheetBloc extends Bloc<FieldActionSheetEvent, FieldActionSheetState> {
final FieldService fieldService;
FieldActionSheetBloc({required Field field, required this.fieldService})
: super(FieldActionSheetState.initial(FieldTypeOptionData.create()..field_2 = field)) {
FieldActionSheetBloc({required GridFieldPB field, required this.fieldService})
: super(FieldActionSheetState.initial(FieldTypeOptionDataPB.create()..field_2 = field)) {
on<FieldActionSheetEvent>(
(event, emit) async {
await event.map(
@ -67,12 +67,12 @@ class FieldActionSheetEvent with _$FieldActionSheetEvent {
@freezed
class FieldActionSheetState with _$FieldActionSheetState {
const factory FieldActionSheetState({
required FieldTypeOptionData fieldTypeOptionData,
required FieldTypeOptionDataPB fieldTypeOptionData,
required String errorText,
required String fieldName,
}) = _FieldActionSheetState;
factory FieldActionSheetState.initial(FieldTypeOptionData data) => FieldActionSheetState(
factory FieldActionSheetState.initial(FieldTypeOptionDataPB data) => FieldActionSheetState(
fieldTypeOptionData: data,
errorText: '',
fieldName: data.field_2.name,

View File

@ -62,7 +62,7 @@ class FieldCellBloc extends Bloc<FieldCellEvent, FieldCellState> {
@freezed
class FieldCellEvent with _$FieldCellEvent {
const factory FieldCellEvent.initial() = _InitialCell;
const factory FieldCellEvent.didReceiveFieldUpdate(Field field) = _DidReceiveFieldUpdate;
const factory FieldCellEvent.didReceiveFieldUpdate(GridFieldPB field) = _DidReceiveFieldUpdate;
const factory FieldCellEvent.startUpdateWidth(double offset) = _StartUpdateWidth;
const factory FieldCellEvent.endUpdateWidth() = _EndUpdateWidth;
}
@ -71,7 +71,7 @@ class FieldCellEvent with _$FieldCellEvent {
class FieldCellState with _$FieldCellState {
const factory FieldCellState({
required String gridId,
required Field field,
required GridFieldPB field,
required double width,
}) = _FieldCellState;

View File

@ -1,3 +1,4 @@
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
@ -6,27 +7,32 @@ import 'package:dartz/dartz.dart';
part 'field_editor_bloc.freezed.dart';
class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
final TypeOptionDataController dataController;
FieldEditorBloc({
required String gridId,
required String fieldName,
required IFieldContextLoader fieldContextLoader,
}) : super(FieldEditorState.initial(gridId, fieldName, fieldContextLoader)) {
required IFieldTypeOptionLoader loader,
}) : dataController = TypeOptionDataController(gridId: gridId, loader: loader),
super(FieldEditorState.initial(gridId, fieldName)) {
on<FieldEditorEvent>(
(event, emit) async {
await event.when(
initial: () async {
final fieldContext = GridFieldContext(gridId: gridId, loader: fieldContextLoader);
await fieldContext.loadData().then((result) {
result.fold(
(l) => emit(state.copyWith(fieldContext: Some(fieldContext), name: fieldContext.field.name)),
(r) => null,
);
dataController.addFieldListener((field) {
if (!isClosed) {
add(FieldEditorEvent.didReceiveFieldChanged(field));
}
});
await dataController.loadData();
},
updateName: (name) {
state.fieldContext.fold(() => null, (fieldContext) => fieldContext.fieldName = name);
dataController.fieldName = name;
emit(state.copyWith(name: name));
},
didReceiveFieldChanged: (GridFieldPB field) {
emit(state.copyWith(field: Some(field)));
},
);
},
);
@ -42,6 +48,7 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
class FieldEditorEvent with _$FieldEditorEvent {
const factory FieldEditorEvent.initial() = _InitialField;
const factory FieldEditorEvent.updateName(String name) = _UpdateName;
const factory FieldEditorEvent.didReceiveFieldChanged(GridFieldPB field) = _DidReceiveFieldChanged;
}
@freezed
@ -50,13 +57,17 @@ class FieldEditorState with _$FieldEditorState {
required String gridId,
required String errorText,
required String name,
required Option<GridFieldContext> fieldContext,
required Option<GridFieldPB> field,
}) = _FieldEditorState;
factory FieldEditorState.initial(String gridId, String fieldName, IFieldContextLoader loader) => FieldEditorState(
factory FieldEditorState.initial(
String gridId,
String fieldName,
) =>
FieldEditorState(
gridId: gridId,
fieldContext: none(),
errorText: '',
field: none(),
name: fieldName,
);
}

View File

@ -1,57 +0,0 @@
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
import 'field_service.dart';
part 'field_editor_pannel_bloc.freezed.dart';
class FieldEditorPannelBloc extends Bloc<FieldEditorPannelEvent, FieldEditorPannelState> {
final GridFieldContext _fieldContext;
void Function()? _fieldListenFn;
FieldEditorPannelBloc(GridFieldContext fieldContext)
: _fieldContext = fieldContext,
super(FieldEditorPannelState.initial(fieldContext)) {
on<FieldEditorPannelEvent>(
(event, emit) async {
event.when(
initial: () {
_fieldListenFn = fieldContext.addFieldListener((field) {
add(FieldEditorPannelEvent.didReceiveFieldUpdated(field));
});
},
didReceiveFieldUpdated: (field) {
emit(state.copyWith(field: field));
},
);
},
);
}
@override
Future<void> close() async {
if (_fieldListenFn != null) {
_fieldContext.removeFieldListener(_fieldListenFn!);
}
return super.close();
}
}
@freezed
class FieldEditorPannelEvent with _$FieldEditorPannelEvent {
const factory FieldEditorPannelEvent.initial() = _Initial;
const factory FieldEditorPannelEvent.didReceiveFieldUpdated(Field field) = _DidReceiveFieldUpdated;
}
@freezed
class FieldEditorPannelState with _$FieldEditorPannelState {
const factory FieldEditorPannelState({
required Field field,
}) = _FieldEditorPannelState;
factory FieldEditorPannelState.initial(GridFieldContext fieldContext) => FieldEditorPannelState(
field: fieldContext.field,
);
}

View File

@ -7,7 +7,7 @@ import 'dart:async';
import 'dart:typed_data';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
typedef UpdateFieldNotifiedValue = Either<Field, FlowyError>;
typedef UpdateFieldNotifiedValue = Either<GridFieldPB, FlowyError>;
class SingleFieldListener {
final String fieldId;
@ -31,7 +31,7 @@ class SingleFieldListener {
switch (ty) {
case GridNotification.DidUpdateField:
result.fold(
(payload) => _updateFieldNotifier?.value = left(Field.fromBuffer(payload)),
(payload) => _updateFieldNotifier?.value = left(GridFieldPB.fromBuffer(payload)),
(error) => _updateFieldNotifier?.value = right(error),
);
break;

View File

@ -1,4 +1,5 @@
import 'package:dartz/dartz.dart';
import 'package:flowy_infra/notifier.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
@ -9,6 +10,10 @@ import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:protobuf/protobuf.dart';
part 'field_service.freezed.dart';
/// FieldService consists of lots of event functions. We define the events in the backend(Rust),
/// you can find the corresponding event implementation in event_map.rs of the corresponding crate.
///
/// You could check out the rust-lib/flowy-grid/event_map.rs for more information.
class FieldService {
final String gridId;
final String fieldId;
@ -16,10 +21,10 @@ class FieldService {
FieldService({required this.gridId, required this.fieldId});
Future<Either<Unit, FlowyError>> moveField(int fromIndex, int toIndex) {
final payload = MoveItemPayload.create()
final payload = MoveItemPayloadPB.create()
..gridId = gridId
..itemId = fieldId
..ty = MoveItemType.MoveField
..ty = MoveItemTypePB.MoveField
..fromIndex = fromIndex
..toIndex = toIndex;
@ -34,7 +39,7 @@ class FieldService {
double? width,
List<int>? typeOptionData,
}) {
var payload = FieldChangesetPayload.create()
var payload = FieldChangesetPayloadPB.create()
..gridId = gridId
..fieldId = fieldId;
@ -68,11 +73,11 @@ class FieldService {
// Create the field if it does not exist. Otherwise, update the field.
static Future<Either<Unit, FlowyError>> insertField({
required String gridId,
required Field field,
required GridFieldPB field,
List<int>? typeOptionData,
String? startFieldId,
}) {
var payload = InsertFieldPayload.create()
var payload = InsertFieldPayloadPB.create()
..gridId = gridId
..field_2 = field
..typeOptionData = typeOptionData ?? [];
@ -89,7 +94,7 @@ class FieldService {
required String fieldId,
required List<int> typeOptionData,
}) {
var payload = UpdateFieldTypeOptionPayload.create()
var payload = UpdateFieldTypeOptionPayloadPB.create()
..gridId = gridId
..fieldId = fieldId
..typeOptionData = typeOptionData;
@ -98,7 +103,7 @@ class FieldService {
}
Future<Either<Unit, FlowyError>> deleteField() {
final payload = FieldIdentifierPayload.create()
final payload = GridFieldIdentifierPayloadPB.create()
..gridId = gridId
..fieldId = fieldId;
@ -106,17 +111,17 @@ class FieldService {
}
Future<Either<Unit, FlowyError>> duplicateField() {
final payload = FieldIdentifierPayload.create()
final payload = GridFieldIdentifierPayloadPB.create()
..gridId = gridId
..fieldId = fieldId;
return GridEventDuplicateField(payload).send();
}
Future<Either<FieldTypeOptionData, FlowyError>> getFieldTypeOptionData({
Future<Either<FieldTypeOptionDataPB, FlowyError>> getFieldTypeOptionData({
required FieldType fieldType,
}) {
final payload = EditFieldPayload.create()
final payload = EditFieldPayloadPB.create()
..gridId = gridId
..fieldId = fieldId
..fieldType = fieldType;
@ -133,16 +138,16 @@ class FieldService {
class GridFieldCellContext with _$GridFieldCellContext {
const factory GridFieldCellContext({
required String gridId,
required Field field,
required GridFieldPB field,
}) = _GridFieldCellContext;
}
abstract class IFieldContextLoader {
abstract class IFieldTypeOptionLoader {
String get gridId;
Future<Either<FieldTypeOptionData, FlowyError>> load();
Future<Either<FieldTypeOptionDataPB, FlowyError>> load();
Future<Either<FieldTypeOptionData, FlowyError>> switchToField(String fieldId, FieldType fieldType) {
final payload = EditFieldPayload.create()
Future<Either<FieldTypeOptionDataPB, FlowyError>> switchToField(String fieldId, FieldType fieldType) {
final payload = EditFieldPayloadPB.create()
..gridId = gridId
..fieldId = fieldId
..fieldType = fieldType;
@ -151,16 +156,16 @@ abstract class IFieldContextLoader {
}
}
class NewFieldContextLoader extends IFieldContextLoader {
class NewFieldTypeOptionLoader extends IFieldTypeOptionLoader {
@override
final String gridId;
NewFieldContextLoader({
NewFieldTypeOptionLoader({
required this.gridId,
});
@override
Future<Either<FieldTypeOptionData, FlowyError>> load() {
final payload = EditFieldPayload.create()
Future<Either<FieldTypeOptionDataPB, FlowyError>> load() {
final payload = EditFieldPayloadPB.create()
..gridId = gridId
..fieldType = FieldType.RichText;
@ -168,19 +173,19 @@ class NewFieldContextLoader extends IFieldContextLoader {
}
}
class FieldContextLoader extends IFieldContextLoader {
class FieldTypeOptionLoader extends IFieldTypeOptionLoader {
@override
final String gridId;
final Field field;
final GridFieldPB field;
FieldContextLoader({
FieldTypeOptionLoader({
required this.gridId,
required this.field,
});
@override
Future<Either<FieldTypeOptionData, FlowyError>> load() {
final payload = EditFieldPayload.create()
Future<Either<FieldTypeOptionDataPB, FlowyError>> load() {
final payload = EditFieldPayloadPB.create()
..gridId = gridId
..fieldId = field.id
..fieldType = field.fieldType;
@ -189,16 +194,16 @@ class FieldContextLoader extends IFieldContextLoader {
}
}
class GridFieldContext {
class TypeOptionDataController {
final String gridId;
final IFieldContextLoader _loader;
final IFieldTypeOptionLoader _loader;
late FieldTypeOptionData _data;
ValueNotifier<Field>? _fieldNotifier;
late FieldTypeOptionDataPB _data;
final PublishNotifier<GridFieldPB> _fieldNotifier = PublishNotifier();
GridFieldContext({
TypeOptionDataController({
required this.gridId,
required IFieldContextLoader loader,
required IFieldTypeOptionLoader loader,
}) : _loader = loader;
Future<Either<Unit, FlowyError>> loadData() async {
@ -207,13 +212,7 @@ class GridFieldContext {
(data) {
data.freeze();
_data = data;
if (_fieldNotifier == null) {
_fieldNotifier = ValueNotifier(data.field_2);
} else {
_fieldNotifier?.value = data.field_2;
}
_fieldNotifier.value = data.field_2;
return left(unit);
},
(err) {
@ -223,9 +222,9 @@ class GridFieldContext {
);
}
Field get field => _data.field_2;
GridFieldPB get field => _data.field_2;
set field(Field field) {
set field(GridFieldPB field) {
_updateData(newField: field);
}
@ -239,7 +238,7 @@ class GridFieldContext {
_updateData(newTypeOptionData: typeOptionData);
}
void _updateData({String? newName, Field? newField, List<int>? newTypeOptionData}) {
void _updateData({String? newName, GridFieldPB? newField, List<int>? newTypeOptionData}) {
_data = _data.rebuild((rebuildData) {
if (newName != null) {
rebuildData.field_2 = rebuildData.field_2.rebuild((rebuildField) {
@ -256,9 +255,7 @@ class GridFieldContext {
}
});
if (_data.field_2 != _fieldNotifier?.value) {
_fieldNotifier?.value = _data.field_2;
}
_fieldNotifier.value = _data.field_2;
FieldService.insertField(
gridId: gridId,
@ -283,16 +280,16 @@ class GridFieldContext {
});
}
void Function() addFieldListener(void Function(Field) callback) {
void Function() addFieldListener(void Function(GridFieldPB) callback) {
listener() {
callback(field);
}
_fieldNotifier?.addListener(listener);
_fieldNotifier.addListener(listener);
return listener;
}
void removeFieldListener(void Function() listener) {
_fieldNotifier?.removeListener(listener);
_fieldNotifier.removeListener(listener);
}
}

View File

@ -0,0 +1,57 @@
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
import 'field_service.dart';
part 'field_type_option_edit_bloc.freezed.dart';
class FieldTypeOptionEditBloc extends Bloc<FieldTypeOptionEditEvent, FieldTypeOptionEditState> {
final TypeOptionDataController _dataController;
void Function()? _fieldListenFn;
FieldTypeOptionEditBloc(TypeOptionDataController dataController)
: _dataController = dataController,
super(FieldTypeOptionEditState.initial(dataController)) {
on<FieldTypeOptionEditEvent>(
(event, emit) async {
event.when(
initial: () {
_fieldListenFn = dataController.addFieldListener((field) {
add(FieldTypeOptionEditEvent.didReceiveFieldUpdated(field));
});
},
didReceiveFieldUpdated: (field) {
emit(state.copyWith(field: field));
},
);
},
);
}
@override
Future<void> close() async {
if (_fieldListenFn != null) {
_dataController.removeFieldListener(_fieldListenFn!);
}
return super.close();
}
}
@freezed
class FieldTypeOptionEditEvent with _$FieldTypeOptionEditEvent {
const factory FieldTypeOptionEditEvent.initial() = _Initial;
const factory FieldTypeOptionEditEvent.didReceiveFieldUpdated(GridFieldPB field) = _DidReceiveFieldUpdated;
}
@freezed
class FieldTypeOptionEditState with _$FieldTypeOptionEditState {
const factory FieldTypeOptionEditState({
required GridFieldPB field,
}) = _FieldTypeOptionEditState;
factory FieldTypeOptionEditState.initial(TypeOptionDataController fieldContext) => FieldTypeOptionEditState(
field: fieldContext.field,
);
}

View File

@ -7,7 +7,7 @@ import 'dart:async';
import 'dart:typed_data';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
typedef UpdateFieldNotifiedValue = Either<GridFieldChangeset, FlowyError>;
typedef UpdateFieldNotifiedValue = Either<GridFieldChangesetPB, FlowyError>;
class GridFieldsListener {
final String gridId;
@ -27,7 +27,7 @@ class GridFieldsListener {
switch (ty) {
case GridNotification.DidUpdateGridField:
result.fold(
(payload) => updateFieldsNotifier?.value = left(GridFieldChangeset.fromBuffer(payload)),
(payload) => updateFieldsNotifier?.value = left(GridFieldChangesetPB.fromBuffer(payload)),
(error) => updateFieldsNotifier?.value = right(error),
);
break;

View File

@ -1,14 +1,15 @@
import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
import 'package:protobuf/protobuf.dart';
part 'date_bloc.freezed.dart';
typedef DateTypeOptionContext = TypeOptionContext<DateTypeOption>;
typedef DateTypeOptionContext = TypeOptionWidgetContext<DateTypeOption>;
class DateTypeOptionDataBuilder extends TypeOptionDataBuilder<DateTypeOption> {
class DateTypeOptionDataParser extends TypeOptionDataParser<DateTypeOption> {
@override
DateTypeOption fromBuffer(List<int> buffer) {
return DateTypeOption.fromBuffer(buffer);

View File

@ -1,4 +1,4 @@
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
@ -7,7 +7,7 @@ import 'package:dartz/dartz.dart';
part 'edit_select_option_bloc.freezed.dart';
class EditSelectOptionBloc extends Bloc<EditSelectOptionEvent, EditSelectOptionState> {
EditSelectOptionBloc({required SelectOption option}) : super(EditSelectOptionState.initial(option)) {
EditSelectOptionBloc({required SelectOptionPB option}) : super(EditSelectOptionState.initial(option)) {
on<EditSelectOptionEvent>(
(event, emit) async {
event.map(
@ -30,14 +30,14 @@ class EditSelectOptionBloc extends Bloc<EditSelectOptionEvent, EditSelectOptionS
return super.close();
}
SelectOption _updateColor(SelectOptionColor color) {
SelectOptionPB _updateColor(SelectOptionColorPB color) {
state.option.freeze();
return state.option.rebuild((option) {
option.color = color;
});
}
SelectOption _updateName(String name) {
SelectOptionPB _updateName(String name) {
state.option.freeze();
return state.option.rebuild((option) {
option.name = name;
@ -48,18 +48,18 @@ class EditSelectOptionBloc extends Bloc<EditSelectOptionEvent, EditSelectOptionS
@freezed
class EditSelectOptionEvent with _$EditSelectOptionEvent {
const factory EditSelectOptionEvent.updateName(String name) = _UpdateName;
const factory EditSelectOptionEvent.updateColor(SelectOptionColor color) = _UpdateColor;
const factory EditSelectOptionEvent.updateColor(SelectOptionColorPB color) = _UpdateColor;
const factory EditSelectOptionEvent.delete() = _Delete;
}
@freezed
class EditSelectOptionState with _$EditSelectOptionState {
const factory EditSelectOptionState({
required SelectOption option,
required SelectOptionPB option,
required Option<bool> deleted,
}) = _EditSelectOptionState;
factory EditSelectOptionState.initial(SelectOption option) => EditSelectOptionState(
factory EditSelectOptionState.initial(SelectOptionPB option) => EditSelectOptionState(
option: option,
deleted: none(),
);

View File

@ -1,26 +1,28 @@
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/multi_select_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
import 'dart:async';
import 'package:protobuf/protobuf.dart';
import 'select_option_type_option_bloc.dart';
import 'type_option_service.dart';
class MultiSelectTypeOptionContext extends TypeOptionContext<MultiSelectTypeOption> with SelectOptionTypeOptionAction {
class MultiSelectTypeOptionContext extends TypeOptionWidgetContext<MultiSelectTypeOption>
with SelectOptionTypeOptionAction {
final TypeOptionService service;
MultiSelectTypeOptionContext({
required MultiSelectTypeOptionDataBuilder dataBuilder,
required GridFieldContext fieldContext,
required MultiSelectTypeOptionWidgetDataParser dataBuilder,
required TypeOptionDataController dataController,
}) : service = TypeOptionService(
gridId: fieldContext.gridId,
fieldId: fieldContext.field.id,
gridId: dataController.gridId,
fieldId: dataController.field.id,
),
super(dataBuilder: dataBuilder, fieldContext: fieldContext);
super(dataParser: dataBuilder, dataController: dataController);
@override
List<SelectOption> Function(SelectOption) get deleteOption {
return (SelectOption option) {
List<SelectOptionPB> Function(SelectOptionPB) get deleteOption {
return (SelectOptionPB option) {
typeOption.freeze();
typeOption = typeOption.rebuild((typeOption) {
final index = typeOption.options.indexWhere((element) => element.id == option.id);
@ -33,7 +35,7 @@ class MultiSelectTypeOptionContext extends TypeOptionContext<MultiSelectTypeOpti
}
@override
Future<List<SelectOption>> Function(String) get insertOption {
Future<List<SelectOptionPB>> Function(String) get insertOption {
return (String optionName) {
return service.newOption(name: optionName).then((result) {
return result.fold(
@ -55,8 +57,8 @@ class MultiSelectTypeOptionContext extends TypeOptionContext<MultiSelectTypeOpti
}
@override
List<SelectOption> Function(SelectOption) get udpateOption {
return (SelectOption option) {
List<SelectOptionPB> Function(SelectOptionPB) get udpateOption {
return (SelectOptionPB option) {
typeOption.freeze();
typeOption = typeOption.rebuild((typeOption) {
final index = typeOption.options.indexWhere((element) => element.id == option.id);
@ -69,7 +71,7 @@ class MultiSelectTypeOptionContext extends TypeOptionContext<MultiSelectTypeOpti
}
}
class MultiSelectTypeOptionDataBuilder extends TypeOptionDataBuilder<MultiSelectTypeOption> {
class MultiSelectTypeOptionWidgetDataParser extends TypeOptionDataParser<MultiSelectTypeOption> {
@override
MultiSelectTypeOption fromBuffer(List<int> buffer) {
return MultiSelectTypeOption.fromBuffer(buffer);

View File

@ -8,9 +8,9 @@ import 'package:protobuf/protobuf.dart';
part 'number_bloc.freezed.dart';
typedef NumberTypeOptionContext = TypeOptionContext<NumberTypeOption>;
typedef NumberTypeOptionContext = TypeOptionWidgetContext<NumberTypeOption>;
class NumberTypeOptionDataBuilder extends TypeOptionDataBuilder<NumberTypeOption> {
class NumberTypeOptionWidgetDataParser extends TypeOptionDataParser<NumberTypeOption> {
@override
NumberTypeOption fromBuffer(List<int> buffer) {
return NumberTypeOption.fromBuffer(buffer);

View File

@ -1,4 +1,4 @@
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
@ -6,25 +6,25 @@ import 'package:dartz/dartz.dart';
part 'select_option_type_option_bloc.freezed.dart';
abstract class SelectOptionTypeOptionAction {
Future<List<SelectOption>> Function(String) get insertOption;
Future<List<SelectOptionPB>> Function(String) get insertOption;
List<SelectOption> Function(SelectOption) get deleteOption;
List<SelectOptionPB> Function(SelectOptionPB) get deleteOption;
List<SelectOption> Function(SelectOption) get udpateOption;
List<SelectOptionPB> Function(SelectOptionPB) get udpateOption;
}
class SelectOptionTypeOptionBloc extends Bloc<SelectOptionTypeOptionEvent, SelectOptionTypeOptionState> {
final SelectOptionTypeOptionAction typeOptionAction;
SelectOptionTypeOptionBloc({
required List<SelectOption> options,
required List<SelectOptionPB> options,
required this.typeOptionAction,
}) : super(SelectOptionTypeOptionState.initial(options)) {
on<SelectOptionTypeOptionEvent>(
(event, emit) async {
await event.when(
createOption: (optionName) async {
final List<SelectOption> options = await typeOptionAction.insertOption(optionName);
final List<SelectOptionPB> options = await typeOptionAction.insertOption(optionName);
emit(state.copyWith(options: options));
},
addingOption: () {
@ -34,11 +34,11 @@ class SelectOptionTypeOptionBloc extends Bloc<SelectOptionTypeOptionEvent, Selec
emit(state.copyWith(isEditingOption: false, newOptionName: none()));
},
updateOption: (option) {
final List<SelectOption> options = typeOptionAction.udpateOption(option);
final List<SelectOptionPB> options = typeOptionAction.udpateOption(option);
emit(state.copyWith(options: options));
},
deleteOption: (option) {
final List<SelectOption> options = typeOptionAction.deleteOption(option);
final List<SelectOptionPB> options = typeOptionAction.deleteOption(option);
emit(state.copyWith(options: options));
},
);
@ -57,19 +57,19 @@ class SelectOptionTypeOptionEvent with _$SelectOptionTypeOptionEvent {
const factory SelectOptionTypeOptionEvent.createOption(String optionName) = _CreateOption;
const factory SelectOptionTypeOptionEvent.addingOption() = _AddingOption;
const factory SelectOptionTypeOptionEvent.endAddingOption() = _EndAddingOption;
const factory SelectOptionTypeOptionEvent.updateOption(SelectOption option) = _UpdateOption;
const factory SelectOptionTypeOptionEvent.deleteOption(SelectOption option) = _DeleteOption;
const factory SelectOptionTypeOptionEvent.updateOption(SelectOptionPB option) = _UpdateOption;
const factory SelectOptionTypeOptionEvent.deleteOption(SelectOptionPB option) = _DeleteOption;
}
@freezed
class SelectOptionTypeOptionState with _$SelectOptionTypeOptionState {
const factory SelectOptionTypeOptionState({
required List<SelectOption> options,
required List<SelectOptionPB> options,
required bool isEditingOption,
required Option<String> newOptionName,
}) = _SelectOptionTyepOptionState;
factory SelectOptionTypeOptionState.initial(List<SelectOption> options) => SelectOptionTypeOptionState(
factory SelectOptionTypeOptionState.initial(List<SelectOptionPB> options) => SelectOptionTypeOptionState(
options: options,
isEditingOption: false,
newOptionName: none(),

View File

@ -1,27 +1,28 @@
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/single_select_type_option.pb.dart';
import 'dart:async';
import 'package:protobuf/protobuf.dart';
import 'select_option_type_option_bloc.dart';
import 'type_option_service.dart';
class SingleSelectTypeOptionContext extends TypeOptionContext<SingleSelectTypeOption>
class SingleSelectTypeOptionContext extends TypeOptionWidgetContext<SingleSelectTypeOptionPB>
with SelectOptionTypeOptionAction {
final TypeOptionService service;
SingleSelectTypeOptionContext({
required SingleSelectTypeOptionDataBuilder dataBuilder,
required GridFieldContext fieldContext,
required SingleSelectTypeOptionWidgetDataParser dataBuilder,
required TypeOptionDataController fieldContext,
}) : service = TypeOptionService(
gridId: fieldContext.gridId,
fieldId: fieldContext.field.id,
),
super(dataBuilder: dataBuilder, fieldContext: fieldContext);
super(dataParser: dataBuilder, dataController: fieldContext);
@override
List<SelectOption> Function(SelectOption) get deleteOption {
return (SelectOption option) {
List<SelectOptionPB> Function(SelectOptionPB) get deleteOption {
return (SelectOptionPB option) {
typeOption.freeze();
typeOption = typeOption.rebuild((typeOption) {
final index = typeOption.options.indexWhere((element) => element.id == option.id);
@ -34,7 +35,7 @@ class SingleSelectTypeOptionContext extends TypeOptionContext<SingleSelectTypeOp
}
@override
Future<List<SelectOption>> Function(String) get insertOption {
Future<List<SelectOptionPB>> Function(String) get insertOption {
return (String optionName) {
return service.newOption(name: optionName).then((result) {
return result.fold(
@ -56,8 +57,8 @@ class SingleSelectTypeOptionContext extends TypeOptionContext<SingleSelectTypeOp
}
@override
List<SelectOption> Function(SelectOption) get udpateOption {
return (SelectOption option) {
List<SelectOptionPB> Function(SelectOptionPB) get udpateOption {
return (SelectOptionPB option) {
typeOption.freeze();
typeOption = typeOption.rebuild((typeOption) {
final index = typeOption.options.indexWhere((element) => element.id == option.id);
@ -70,9 +71,9 @@ class SingleSelectTypeOptionContext extends TypeOptionContext<SingleSelectTypeOp
}
}
class SingleSelectTypeOptionDataBuilder extends TypeOptionDataBuilder<SingleSelectTypeOption> {
class SingleSelectTypeOptionWidgetDataParser extends TypeOptionDataParser<SingleSelectTypeOptionPB> {
@override
SingleSelectTypeOption fromBuffer(List<int> buffer) {
return SingleSelectTypeOption.fromBuffer(buffer);
SingleSelectTypeOptionPB fromBuffer(List<int> buffer) {
return SingleSelectTypeOptionPB.fromBuffer(buffer);
}
}

View File

@ -6,7 +6,7 @@ import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
import 'package:protobuf/protobuf.dart';
class TypeOptionService {
@ -18,14 +18,14 @@ class TypeOptionService {
required this.fieldId,
});
Future<Either<SelectOption, FlowyError>> newOption({
Future<Either<SelectOptionPB, FlowyError>> newOption({
required String name,
}) {
final fieldIdentifier = FieldIdentifierPayload.create()
final fieldIdentifier = GridFieldIdentifierPayloadPB.create()
..gridId = gridId
..fieldId = fieldId;
final payload = CreateSelectOptionPayload.create()
final payload = CreateSelectOptionPayloadPB.create()
..optionName = name
..fieldIdentifier = fieldIdentifier;
@ -33,36 +33,36 @@ class TypeOptionService {
}
}
abstract class TypeOptionDataBuilder<T> {
abstract class TypeOptionDataParser<T> {
T fromBuffer(List<int> buffer);
}
class TypeOptionContext<T extends GeneratedMessage> {
class TypeOptionWidgetContext<T extends GeneratedMessage> {
T? _typeOptionObject;
final GridFieldContext _fieldContext;
final TypeOptionDataBuilder<T> dataBuilder;
final TypeOptionDataController _dataController;
final TypeOptionDataParser<T> dataParser;
TypeOptionContext({
required this.dataBuilder,
required GridFieldContext fieldContext,
}) : _fieldContext = fieldContext;
TypeOptionWidgetContext({
required this.dataParser,
required TypeOptionDataController dataController,
}) : _dataController = dataController;
String get gridId => _fieldContext.gridId;
String get gridId => _dataController.gridId;
Field get field => _fieldContext.field;
GridFieldPB get field => _dataController.field;
T get typeOption {
if (_typeOptionObject != null) {
return _typeOptionObject!;
}
final T object = dataBuilder.fromBuffer(_fieldContext.typeOptionData);
final T object = dataParser.fromBuffer(_dataController.typeOptionData);
_typeOptionObject = object;
return object;
}
set typeOption(T typeOption) {
_fieldContext.typeOptionData = typeOption.writeToBuffer();
_dataController.typeOptionData = typeOption.writeToBuffer();
_typeOptionObject = typeOption;
}
}
@ -74,10 +74,10 @@ abstract class TypeOptionFieldDelegate {
class TypeOptionContext2<T> {
final String gridId;
final Field field;
final GridFieldPB field;
final FieldService _fieldService;
T? _data;
final TypeOptionDataBuilder dataBuilder;
final TypeOptionDataParser dataBuilder;
TypeOptionContext2({
required this.gridId,

View File

@ -7,7 +7,7 @@ import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'block/block_service.dart';
import 'block/block_cache.dart';
import 'grid_service.dart';
import 'row/row_service.dart';
import 'dart:collection';
@ -20,17 +20,17 @@ class GridBloc extends Bloc<GridEvent, GridState> {
final GridFieldCache fieldCache;
// key: the block id
final LinkedHashMap<String, GridBlockCacheService> _blocks;
final LinkedHashMap<String, GridBlockCache> _blocks;
List<GridRow> get rows {
final List<GridRow> rows = [];
List<GridRowInfo> get rowInfos {
final List<GridRowInfo> rows = [];
for (var block in _blocks.values) {
rows.addAll(block.rows);
}
return rows;
}
GridBloc({required View view})
GridBloc({required ViewPB view})
: gridId = view.id,
_blocks = LinkedHashMap.identity(),
_gridService = GridService(gridId: view.id),
@ -46,11 +46,11 @@ class GridBloc extends Bloc<GridEvent, GridState> {
createRow: () {
_gridService.createRow();
},
didReceiveRowUpdate: (rows, reason) {
emit(state.copyWith(rows: rows, reason: reason));
didReceiveRowUpdate: (newRowInfos, reason) {
emit(state.copyWith(rowInfos: newRowInfos, reason: reason));
},
didReceiveFieldUpdate: (fields) {
emit(state.copyWith(rows: rows, fields: GridFieldEquatable(fields)));
emit(state.copyWith(rowInfos: rowInfos, fields: GridFieldEquatable(fields)));
},
);
},
@ -68,8 +68,8 @@ class GridBloc extends Bloc<GridEvent, GridState> {
return super.close();
}
GridRowCacheService? getRowCache(String blockId, String rowId) {
final GridBlockCacheService? blockCache = _blocks[blockId];
GridRowCache? getRowCache(String blockId, String rowId) {
final GridBlockCache? blockCache = _blocks[blockId];
return blockCache?.rowCache;
}
@ -93,8 +93,8 @@ class GridBloc extends Bloc<GridEvent, GridState> {
);
}
Future<void> _loadFields(Grid grid, Emitter<GridState> emit) async {
final result = await _gridService.getFields(fieldOrders: grid.fieldOrders);
Future<void> _loadFields(GridPB grid, Emitter<GridState> emit) async {
final result = await _gridService.getFields(fieldIds: grid.fields);
return Future(
() => result.fold(
(fields) {
@ -103,7 +103,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
emit(state.copyWith(
grid: Some(grid),
fields: GridFieldEquatable(fieldCache.fields),
rows: rows,
rowInfos: rowInfos,
loadingState: GridLoadingState.finish(left(unit)),
));
},
@ -112,14 +112,14 @@ class GridBloc extends Bloc<GridEvent, GridState> {
);
}
void _initialBlocks(List<GridBlock> blocks) {
void _initialBlocks(List<GridBlockPB> blocks) {
for (final block in blocks) {
if (_blocks[block.id] != null) {
Log.warn("Intial duplicate block's cache: ${block.id}");
return;
}
final cache = GridBlockCacheService(
final cache = GridBlockCache(
gridId: gridId,
block: block,
fieldCache: fieldCache,
@ -127,7 +127,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
cache.addListener(
listenWhen: () => !isClosed,
onChangeReason: (reason) => add(GridEvent.didReceiveRowUpdate(rows, reason)),
onChangeReason: (reason) => add(GridEvent.didReceiveRowUpdate(rowInfos, reason)),
);
_blocks[block.id] = cache;
@ -139,24 +139,25 @@ class GridBloc extends Bloc<GridEvent, GridState> {
class GridEvent with _$GridEvent {
const factory GridEvent.initial() = InitialGrid;
const factory GridEvent.createRow() = _CreateRow;
const factory GridEvent.didReceiveRowUpdate(List<GridRow> rows, GridRowChangeReason listState) = _DidReceiveRowUpdate;
const factory GridEvent.didReceiveFieldUpdate(List<Field> fields) = _DidReceiveFieldUpdate;
const factory GridEvent.didReceiveRowUpdate(List<GridRowInfo> rows, GridRowChangeReason listState) =
_DidReceiveRowUpdate;
const factory GridEvent.didReceiveFieldUpdate(List<GridFieldPB> fields) = _DidReceiveFieldUpdate;
}
@freezed
class GridState with _$GridState {
const factory GridState({
required String gridId,
required Option<Grid> grid,
required Option<GridPB> grid,
required GridFieldEquatable fields,
required List<GridRow> rows,
required List<GridRowInfo> rowInfos,
required GridLoadingState loadingState,
required GridRowChangeReason reason,
}) = _GridState;
factory GridState.initial(String gridId) => GridState(
fields: const GridFieldEquatable([]),
rows: [],
rowInfos: [],
grid: none(),
gridId: gridId,
loadingState: const _Loading(),
@ -171,8 +172,8 @@ class GridLoadingState with _$GridLoadingState {
}
class GridFieldEquatable extends Equatable {
final List<Field> _fields;
const GridFieldEquatable(List<Field> fields) : _fields = fields;
final List<GridFieldPB> _fields;
const GridFieldEquatable(List<GridFieldPB> fields) : _fields = fields;
@override
List<Object?> get props {
@ -182,5 +183,5 @@ class GridFieldEquatable extends Equatable {
];
}
UnmodifiableListView<Field> get value => UnmodifiableListView(_fields);
UnmodifiableListView<GridFieldPB> get value => UnmodifiableListView(_fields);
}

View File

@ -34,7 +34,7 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
}
Future<void> _moveField(_MoveField value, Emitter<GridHeaderState> emit) async {
final fields = List<Field>.from(state.fields);
final fields = List<GridFieldPB>.from(state.fields);
fields.insert(value.toIndex, fields.removeAt(value.fromIndex));
emit(state.copyWith(fields: fields));
@ -62,16 +62,16 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
@freezed
class GridHeaderEvent with _$GridHeaderEvent {
const factory GridHeaderEvent.initial() = _InitialHeader;
const factory GridHeaderEvent.didReceiveFieldUpdate(List<Field> fields) = _DidReceiveFieldUpdate;
const factory GridHeaderEvent.moveField(Field field, int fromIndex, int toIndex) = _MoveField;
const factory GridHeaderEvent.didReceiveFieldUpdate(List<GridFieldPB> fields) = _DidReceiveFieldUpdate;
const factory GridHeaderEvent.moveField(GridFieldPB field, int fromIndex, int toIndex) = _MoveField;
}
@freezed
class GridHeaderState with _$GridHeaderState {
const factory GridHeaderState({required List<Field> fields}) = _GridHeaderState;
const factory GridHeaderState({required List<GridFieldPB> fields}) = _GridHeaderState;
factory GridHeaderState.initial(List<Field> fields) {
// final List<Field> newFields = List.from(fields);
factory GridHeaderState.initial(List<GridFieldPB> fields) {
// final List<GridFieldPB> newFields = List.from(fields);
// newFields.retainWhere((field) => field.visibility);
return GridHeaderState(fields: fields);
}

View File

@ -19,55 +19,54 @@ class GridService {
required this.gridId,
});
Future<Either<Grid, FlowyError>> loadGrid() async {
await FolderEventSetLatestView(ViewId(value: gridId)).send();
Future<Either<GridPB, FlowyError>> loadGrid() async {
await FolderEventSetLatestView(ViewIdPB(value: gridId)).send();
final payload = GridId(value: gridId);
final payload = GridIdPB(value: gridId);
return GridEventGetGrid(payload).send();
}
Future<Either<Row, FlowyError>> createRow({Option<String>? startRowId}) {
CreateRowPayload payload = CreateRowPayload.create()..gridId = gridId;
Future<Either<GridRowPB, FlowyError>> createRow({Option<String>? startRowId}) {
CreateRowPayloadPB payload = CreateRowPayloadPB.create()..gridId = gridId;
startRowId?.fold(() => null, (id) => payload.startRowId = id);
return GridEventCreateRow(payload).send();
}
Future<Either<RepeatedField, FlowyError>> getFields({required List<FieldOrder> fieldOrders}) {
final payload = QueryFieldPayload.create()
Future<Either<RepeatedGridFieldPB, FlowyError>> getFields({required List<GridFieldIdPB> fieldIds}) {
final payload = QueryFieldPayloadPB.create()
..gridId = gridId
..fieldOrders = RepeatedFieldOrder(items: fieldOrders);
..fieldIds = RepeatedGridFieldIdPB(items: fieldIds);
return GridEventGetFields(payload).send();
}
Future<Either<Unit, FlowyError>> closeGrid() {
final request = ViewId(value: gridId);
final request = ViewIdPB(value: gridId);
return FolderEventCloseView(request).send();
}
}
class FieldsNotifier extends ChangeNotifier {
List<Field> _fields = [];
List<GridFieldPB> _fields = [];
set fields(List<Field> fields) {
set fields(List<GridFieldPB> fields) {
_fields = fields;
notifyListeners();
}
List<Field> get fields => _fields;
List<GridFieldPB> get fields => _fields;
}
typedef FieldChangesetCallback = void Function(GridFieldChangeset);
typedef FieldsCallback = void Function(List<Field>);
typedef FieldChangesetCallback = void Function(GridFieldChangesetPB);
typedef FieldsCallback = void Function(List<GridFieldPB>);
class GridFieldCache {
final String gridId;
late final GridFieldsListener _fieldListener;
final GridFieldsListener _fieldListener;
FieldsNotifier? _fieldNotifier = FieldsNotifier();
final Map<FieldsCallback, VoidCallback> _fieldsCallbackMap = {};
final Map<FieldChangesetCallback, FieldChangesetCallback> _changesetCallbackMap = {};
GridFieldCache({required this.gridId}) {
_fieldListener = GridFieldsListener(gridId: gridId);
GridFieldCache({required this.gridId}) : _fieldListener = GridFieldsListener(gridId: gridId) {
_fieldListener.start(onFieldsChanged: (result) {
result.fold(
(changeset) {
@ -89,11 +88,11 @@ class GridFieldCache {
_fieldNotifier = null;
}
UnmodifiableListView<Field> get unmodifiableFields => UnmodifiableListView(_fieldNotifier?.fields ?? []);
UnmodifiableListView<GridFieldPB> get unmodifiableFields => UnmodifiableListView(_fieldNotifier?.fields ?? []);
List<Field> get fields => [..._fieldNotifier?.fields ?? []];
List<GridFieldPB> get fields => [..._fieldNotifier?.fields ?? []];
set fields(List<Field> fields) {
set fields(List<GridFieldPB> fields) {
_fieldNotifier?.fields = [...fields];
}
@ -142,12 +141,12 @@ class GridFieldCache {
}
}
void _deleteFields(List<FieldOrder> deletedFields) {
void _deleteFields(List<GridFieldIdPB> deletedFields) {
if (deletedFields.isEmpty) {
return;
}
final List<Field> newFields = fields;
final Map<String, FieldOrder> deletedFieldMap = {
final List<GridFieldPB> newFields = fields;
final Map<String, GridFieldIdPB> deletedFieldMap = {
for (var fieldOrder in deletedFields) fieldOrder.fieldId: fieldOrder
};
@ -155,11 +154,11 @@ class GridFieldCache {
_fieldNotifier?.fields = newFields;
}
void _insertFields(List<IndexField> insertedFields) {
void _insertFields(List<IndexFieldPB> insertedFields) {
if (insertedFields.isEmpty) {
return;
}
final List<Field> newFields = fields;
final List<GridFieldPB> newFields = fields;
for (final indexField in insertedFields) {
if (newFields.length > indexField.index) {
newFields.insert(indexField.index, indexField.field_1);
@ -170,11 +169,11 @@ class GridFieldCache {
_fieldNotifier?.fields = newFields;
}
void _updateFields(List<Field> updatedFields) {
void _updateFields(List<GridFieldPB> updatedFields) {
if (updatedFields.isEmpty) {
return;
}
final List<Field> newFields = fields;
final List<GridFieldPB> newFields = fields;
for (final updatedField in updatedFields) {
final index = newFields.indexWhere((field) => field.id == updatedField.id);
if (index != -1) {
@ -186,14 +185,14 @@ class GridFieldCache {
}
}
class GridRowCacheDelegateImpl extends GridRowCacheDelegate {
class GridRowCacheFieldNotifierImpl extends GridRowCacheFieldNotifier {
final GridFieldCache _cache;
FieldChangesetCallback? _onChangesetFn;
FieldsCallback? _onFieldFn;
GridRowCacheDelegateImpl(GridFieldCache cache) : _cache = cache;
GridRowCacheFieldNotifierImpl(GridFieldCache cache) : _cache = cache;
@override
UnmodifiableListView<Field> get fields => _cache.unmodifiableFields;
UnmodifiableListView<GridFieldPB> get fields => _cache.unmodifiableFields;
@override
void onFieldsChanged(VoidCallback callback) {
@ -202,8 +201,8 @@ class GridRowCacheDelegateImpl extends GridRowCacheDelegate {
}
@override
void onFieldUpdated(void Function(Field) callback) {
_onChangesetFn = (GridFieldChangeset changeset) {
void onFieldChanged(void Function(GridFieldPB) callback) {
_onChangesetFn = (GridFieldChangesetPB changeset) {
for (final updatedField in changeset.updatedFields) {
callback(updatedField);
}

View File

@ -4,18 +4,18 @@ export 'row/row_service.dart';
export 'grid_service.dart';
export 'grid_header_bloc.dart';
// Field
// GridFieldPB
export 'field/field_service.dart';
export 'field/field_action_sheet_bloc.dart';
export 'field/field_editor_bloc.dart';
export 'field/field_editor_pannel_bloc.dart';
export 'field/field_type_option_edit_bloc.dart';
// Field Type Option
// GridFieldPB Type Option
export 'field/type_option/date_bloc.dart';
export 'field/type_option/number_bloc.dart';
export 'field/type_option/single_select_type_option.dart';
// Cell
// GridCellPB
export 'cell/text_cell_bloc.dart';
export 'cell/number_cell_bloc.dart';
export 'cell/select_option_cell_bloc.dart';

View File

@ -11,11 +11,11 @@ part 'row_action_sheet_bloc.freezed.dart';
class RowActionSheetBloc extends Bloc<RowActionSheetEvent, RowActionSheetState> {
final RowService _rowService;
RowActionSheetBloc({required GridRow rowData})
RowActionSheetBloc({required GridRowInfo rowData})
: _rowService = RowService(
gridId: rowData.gridId,
blockId: rowData.blockId,
rowId: rowData.rowId,
rowId: rowData.id,
),
super(RowActionSheetState.initial(rowData)) {
on<RowActionSheetEvent>(
@ -53,10 +53,10 @@ class RowActionSheetEvent with _$RowActionSheetEvent {
@freezed
class RowActionSheetState with _$RowActionSheetState {
const factory RowActionSheetState({
required GridRow rowData,
required GridRowInfo rowData,
}) = _RowActionSheetState;
factory RowActionSheetState.initial(GridRow rowData) => RowActionSheetState(
factory RowActionSheetState.initial(GridRowInfo rowData) => RowActionSheetState(
rowData: rowData,
);
}

View File

@ -11,19 +11,19 @@ part 'row_bloc.freezed.dart';
class RowBloc extends Bloc<RowEvent, RowState> {
final RowService _rowService;
final GridRowCacheService _rowCache;
final GridRowCache _rowCache;
void Function()? _rowListenFn;
RowBloc({
required GridRow rowData,
required GridRowCacheService rowCache,
required GridRowInfo rowInfo,
required GridRowCache rowCache,
}) : _rowService = RowService(
gridId: rowData.gridId,
blockId: rowData.blockId,
rowId: rowData.rowId,
gridId: rowInfo.gridId,
blockId: rowInfo.blockId,
rowId: rowInfo.id,
),
_rowCache = rowCache,
super(RowState.initial(rowData, rowCache.loadGridCells(rowData.rowId))) {
super(RowState.initial(rowInfo, rowCache.loadGridCells(rowInfo.id))) {
on<RowEvent>(
(event, emit) async {
await event.map(
@ -58,7 +58,7 @@ class RowBloc extends Bloc<RowEvent, RowState> {
Future<void> _startListening() async {
_rowListenFn = _rowCache.addListener(
rowId: state.rowData.rowId,
rowId: state.rowInfo.id,
onCellUpdated: (cellDatas, reason) => add(RowEvent.didReceiveCellDatas(cellDatas, reason)),
listenWhen: () => !isClosed,
);
@ -76,23 +76,23 @@ class RowEvent with _$RowEvent {
@freezed
class RowState with _$RowState {
const factory RowState({
required GridRow rowData,
required GridRowInfo rowInfo,
required GridCellMap gridCellMap,
required UnmodifiableListView<GridCellEquatable> snapshots,
GridRowChangeReason? changeReason,
}) = _RowState;
factory RowState.initial(GridRow rowData, GridCellMap cellDataMap) => RowState(
rowData: rowData,
factory RowState.initial(GridRowInfo rowInfo, GridCellMap cellDataMap) => RowState(
rowInfo: rowInfo,
gridCellMap: cellDataMap,
snapshots: UnmodifiableListView(cellDataMap.values.map((e) => GridCellEquatable(e.field)).toList()),
);
}
class GridCellEquatable extends Equatable {
final Field _field;
final GridFieldPB _field;
const GridCellEquatable(Field field) : _field = field;
const GridCellEquatable(GridFieldPB field) : _field = field;
@override
List<Object?> get props => [

View File

@ -7,13 +7,13 @@ import 'row_service.dart';
part 'row_detail_bloc.freezed.dart';
class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
final GridRow rowData;
final GridRowCacheService _rowCache;
final GridRowInfo rowInfo;
final GridRowCache _rowCache;
void Function()? _rowListenFn;
RowDetailBloc({
required this.rowData,
required GridRowCacheService rowCache,
required this.rowInfo,
required GridRowCache rowCache,
}) : _rowCache = rowCache,
super(RowDetailState.initial()) {
on<RowDetailEvent>(
@ -41,14 +41,14 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
Future<void> _startListening() async {
_rowListenFn = _rowCache.addListener(
rowId: rowData.rowId,
rowId: rowInfo.id,
onCellUpdated: (cellDatas, reason) => add(RowDetailEvent.didReceiveCellDatas(cellDatas.values.toList())),
listenWhen: () => !isClosed,
);
}
Future<void> _loadCellData() async {
final cellDataMap = _rowCache.loadGridCells(rowData.rowId);
final cellDataMap = _rowCache.loadGridCells(rowInfo.id);
if (!isClosed) {
add(RowDetailEvent.didReceiveCellDatas(cellDataMap.values.toList()));
}
@ -58,13 +58,13 @@ class RowDetailBloc extends Bloc<RowDetailEvent, RowDetailState> {
@freezed
class RowDetailEvent with _$RowDetailEvent {
const factory RowDetailEvent.initial() = _Initial;
const factory RowDetailEvent.didReceiveCellDatas(List<GridCell> gridCells) = _DidReceiveCellDatas;
const factory RowDetailEvent.didReceiveCellDatas(List<GridCellIdentifier> gridCells) = _DidReceiveCellDatas;
}
@freezed
class RowDetailState with _$RowDetailState {
const factory RowDetailState({
required List<GridCell> gridCells,
required List<GridCellIdentifier> gridCells,
}) = _RowDetailState;
factory RowDetailState.initial() => RowDetailState(

View File

@ -8,8 +8,8 @@ import 'dart:typed_data';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
typedef UpdateRowNotifiedValue = Either<Row, FlowyError>;
typedef UpdateFieldNotifiedValue = Either<List<Field>, FlowyError>;
typedef UpdateRowNotifiedValue = Either<GridRowPB, FlowyError>;
typedef UpdateFieldNotifiedValue = Either<List<GridFieldPB>, FlowyError>;
class RowListener {
final String rowId;
@ -26,7 +26,7 @@ class RowListener {
switch (ty) {
case GridNotification.DidUpdateRow:
result.fold(
(payload) => updateRowNotifier?.value = left(Row.fromBuffer(payload)),
(payload) => updateRowNotifier?.value = left(GridRowPB.fromBuffer(payload)),
(error) => updateRowNotifier?.value = right(error),
);
break;

View File

@ -1,5 +1,4 @@
import 'dart:collection';
import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
@ -15,118 +14,135 @@ part 'row_service.freezed.dart';
typedef RowUpdateCallback = void Function();
abstract class GridRowCacheDelegate with GridCellCacheDelegate {
UnmodifiableListView<Field> get fields;
void onFieldsChanged(void Function() callback);
abstract class GridRowCacheFieldNotifier {
UnmodifiableListView<GridFieldPB> get fields;
void onFieldsChanged(VoidCallback callback);
void onFieldChanged(void Function(GridFieldPB) callback);
void dispose();
}
class GridRowCacheService {
/// Cache the rows in memory
/// Insert / delete / update row
///
/// Read https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/frontend/grid for more information.
class GridRowCache {
final String gridId;
final GridBlock block;
final _Notifier _notifier;
List<GridRow> _rows = [];
final HashMap<String, Row> _rowByRowId;
final GridRowCacheDelegate _delegate;
final GridCellCacheService _cellCache;
final GridBlockPB block;
List<GridRow> get rows => _rows;
GridCellCacheService get cellCache => _cellCache;
/// _rows containers the current block's rows
/// Use List to reverse the order of the GridRow.
List<GridRowInfo> _rowInfos = [];
GridRowCacheService({
/// Use Map for faster access the raw row data.
final HashMap<String, GridRowPB> _rowByRowId;
final GridCellCache _cellCache;
final GridRowCacheFieldNotifier _fieldNotifier;
final _GridRowChangesetNotifier _rowChangeReasonNotifier;
UnmodifiableListView<GridRowInfo> get rows => UnmodifiableListView(_rowInfos);
GridCellCache get cellCache => _cellCache;
GridRowCache({
required this.gridId,
required this.block,
required GridRowCacheDelegate delegate,
}) : _cellCache = GridCellCacheService(gridId: gridId, delegate: delegate),
required GridRowCacheFieldNotifier notifier,
}) : _cellCache = GridCellCache(gridId: gridId),
_rowByRowId = HashMap(),
_notifier = _Notifier(),
_delegate = delegate {
_rowChangeReasonNotifier = _GridRowChangesetNotifier(),
_fieldNotifier = notifier {
//
delegate.onFieldsChanged(() => _notifier.receive(const GridRowChangeReason.fieldDidChange()));
_rows = block.rowInfos.map((rowInfo) => buildGridRow(rowInfo)).toList();
notifier.onFieldsChanged(() => _rowChangeReasonNotifier.receive(const GridRowChangeReason.fieldDidChange()));
notifier.onFieldChanged((field) => _cellCache.remove(field.id));
_rowInfos = block.rows.map((rowInfo) => buildGridRow(rowInfo.id, rowInfo.height.toDouble())).toList();
}
Future<void> dispose() async {
_delegate.dispose();
_notifier.dispose();
_fieldNotifier.dispose();
_rowChangeReasonNotifier.dispose();
await _cellCache.dispose();
}
void applyChangesets(List<GridRowsChangeset> changesets) {
void applyChangesets(List<GridBlockChangesetPB> changesets) {
for (final changeset in changesets) {
_deleteRows(changeset.deletedRows);
_insertRows(changeset.insertedRows);
_updateRows(changeset.updatedRows);
_hideRows(changeset.hideRows);
_showRows(changeset.visibleRows);
}
}
void _deleteRows(List<GridRowId> deletedRows) {
void _deleteRows(List<String> deletedRows) {
if (deletedRows.isEmpty) {
return;
}
final List<GridRow> newRows = [];
final List<GridRowInfo> newRows = [];
final DeletedIndexs deletedIndex = [];
final Map<String, GridRowId> deletedRowByRowId = {for (var e in deletedRows) e.rowId: e};
final Map<String, String> deletedRowByRowId = {for (var rowId in deletedRows) rowId: rowId};
_rows.asMap().forEach((index, row) {
if (deletedRowByRowId[row.rowId] == null) {
_rowInfos.asMap().forEach((index, row) {
if (deletedRowByRowId[row.id] == null) {
newRows.add(row);
} else {
_rowByRowId.remove(row.id);
deletedIndex.add(DeletedIndex(index: index, row: row));
}
});
_rows = newRows;
_notifier.receive(GridRowChangeReason.delete(deletedIndex));
_rowInfos = newRows;
_rowChangeReasonNotifier.receive(GridRowChangeReason.delete(deletedIndex));
}
void _insertRows(List<IndexRowOrder> insertRows) {
void _insertRows(List<InsertedRowPB> insertRows) {
if (insertRows.isEmpty) {
return;
}
InsertedIndexs insertIndexs = [];
final List<GridRow> newRows = _rows;
for (final insertRow in insertRows) {
final insertIndex = InsertedIndex(
index: insertRow.index,
rowId: insertRow.rowInfo.rowId,
rowId: insertRow.rowId,
);
insertIndexs.add(insertIndex);
newRows.insert(insertRow.index, (buildGridRow(insertRow.rowInfo)));
_rowInfos.insert(insertRow.index, (buildGridRow(insertRow.rowId, insertRow.height.toDouble())));
}
_notifier.receive(GridRowChangeReason.insert(insertIndexs));
_rowChangeReasonNotifier.receive(GridRowChangeReason.insert(insertIndexs));
}
void _updateRows(List<UpdatedRowOrder> updatedRows) {
void _updateRows(List<UpdatedRowPB> updatedRows) {
if (updatedRows.isEmpty) {
return;
}
final UpdatedIndexs updatedIndexs = UpdatedIndexs();
final List<GridRow> newRows = _rows;
for (final updatedRow in updatedRows) {
final rowOrder = updatedRow.rowInfo;
final rowId = updatedRow.rowInfo.rowId;
final index = newRows.indexWhere((row) => row.rowId == rowId);
final rowId = updatedRow.rowId;
final index = _rowInfos.indexWhere((row) => row.id == rowId);
if (index != -1) {
_rowByRowId[rowId] = updatedRow.row;
newRows.removeAt(index);
newRows.insert(index, buildGridRow(rowOrder));
_rowInfos.removeAt(index);
_rowInfos.insert(index, buildGridRow(rowId, updatedRow.row.height.toDouble()));
updatedIndexs[rowId] = UpdatedIndex(index: index, rowId: rowId);
}
}
_notifier.receive(GridRowChangeReason.update(updatedIndexs));
_rowChangeReasonNotifier.receive(GridRowChangeReason.update(updatedIndexs));
}
void _hideRows(List<String> hideRows) {}
void _showRows(List<String> visibleRows) {}
void onRowsChanged(
void Function(GridRowChangeReason) onRowChanged,
) {
_notifier.addListener(() {
onRowChanged(_notifier._reason);
_rowChangeReasonNotifier.addListener(() {
onRowChanged(_rowChangeReasonNotifier.reason);
});
}
@ -145,12 +161,12 @@ class GridRowCacheService {
final row = _rowByRowId[rowId];
if (row != null) {
final GridCellMap cellDataMap = _makeGridCells(rowId, row);
onCellUpdated(cellDataMap, _notifier._reason);
onCellUpdated(cellDataMap, _rowChangeReasonNotifier.reason);
}
}
}
_notifier._reason.whenOrNull(
_rowChangeReasonNotifier.reason.whenOrNull(
update: (indexs) {
if (indexs[rowId] != null) notifyUpdate();
},
@ -158,16 +174,16 @@ class GridRowCacheService {
);
}
_notifier.addListener(listenrHandler);
_rowChangeReasonNotifier.addListener(listenrHandler);
return listenrHandler;
}
void removeRowListener(VoidCallback callback) {
_notifier.removeListener(callback);
_rowChangeReasonNotifier.removeListener(callback);
}
GridCellMap loadGridCells(String rowId) {
final Row? data = _rowByRowId[rowId];
final GridRowPB? data = _rowByRowId[rowId];
if (data == null) {
_loadRow(rowId);
}
@ -175,7 +191,7 @@ class GridRowCacheService {
}
Future<void> _loadRow(String rowId) async {
final payload = GridRowIdPayload.create()
final payload = GridRowIdPayloadPB.create()
..gridId = gridId
..blockId = block.id
..rowId = rowId;
@ -187,11 +203,11 @@ class GridRowCacheService {
);
}
GridCellMap _makeGridCells(String rowId, Row? row) {
GridCellMap _makeGridCells(String rowId, GridRowPB? row) {
var cellDataMap = GridCellMap.new();
for (final field in _delegate.fields) {
for (final field in _fieldNotifier.fields) {
if (field.visibility) {
cellDataMap[field.id] = GridCell(
cellDataMap[field.id] = GridCellIdentifier(
rowId: rowId,
gridId: gridId,
field: field,
@ -201,7 +217,7 @@ class GridRowCacheService {
return cellDataMap;
}
void _refreshRow(OptionalRow optionRow) {
void _refreshRow(OptionalRowPB optionRow) {
if (!optionRow.hasRow()) {
return;
}
@ -209,41 +225,41 @@ class GridRowCacheService {
updatedRow.freeze();
_rowByRowId[updatedRow.id] = updatedRow;
final index = _rows.indexWhere((gridRow) => gridRow.rowId == updatedRow.id);
final index = _rowInfos.indexWhere((gridRow) => gridRow.id == updatedRow.id);
if (index != -1) {
// update the corresponding row in _rows if they are not the same
if (_rows[index].data != updatedRow) {
final row = _rows.removeAt(index).copyWith(data: updatedRow);
_rows.insert(index, row);
if (_rowInfos[index].rawRow != updatedRow) {
final row = _rowInfos.removeAt(index).copyWith(rawRow: updatedRow);
_rowInfos.insert(index, row);
// Calculate the update index
final UpdatedIndexs updatedIndexs = UpdatedIndexs();
updatedIndexs[row.rowId] = UpdatedIndex(index: index, rowId: row.rowId);
updatedIndexs[row.id] = UpdatedIndex(index: index, rowId: row.id);
//
_notifier.receive(GridRowChangeReason.update(updatedIndexs));
_rowChangeReasonNotifier.receive(GridRowChangeReason.update(updatedIndexs));
}
}
}
GridRow buildGridRow(BlockRowInfo rowInfo) {
return GridRow(
GridRowInfo buildGridRow(String rowId, double rowHeight) {
return GridRowInfo(
gridId: gridId,
blockId: block.id,
fields: _delegate.fields,
rowId: rowInfo.rowId,
height: rowInfo.height.toDouble(),
fields: _fieldNotifier.fields,
id: rowId,
height: rowHeight,
);
}
}
class _Notifier extends ChangeNotifier {
GridRowChangeReason _reason = const InitialListState();
class _GridRowChangesetNotifier extends ChangeNotifier {
GridRowChangeReason reason = const InitialListState();
_Notifier();
_GridRowChangesetNotifier();
void receive(GridRowChangeReason reason) {
_reason = reason;
void receive(GridRowChangeReason newReason) {
reason = newReason;
reason.map(
insert: (_) => notifyListeners(),
delete: (_) => notifyListeners(),
@ -261,8 +277,8 @@ class RowService {
RowService({required this.gridId, required this.blockId, required this.rowId});
Future<Either<Row, FlowyError>> createRow() {
CreateRowPayload payload = CreateRowPayload.create()
Future<Either<GridRowPB, FlowyError>> createRow() {
CreateRowPayloadPB payload = CreateRowPayloadPB.create()
..gridId = gridId
..startRowId = rowId;
@ -270,18 +286,18 @@ class RowService {
}
Future<Either<Unit, FlowyError>> moveRow(String rowId, int fromIndex, int toIndex) {
final payload = MoveItemPayload.create()
final payload = MoveItemPayloadPB.create()
..gridId = gridId
..itemId = rowId
..ty = MoveItemType.MoveRow
..ty = MoveItemTypePB.MoveRow
..fromIndex = fromIndex
..toIndex = toIndex;
return GridEventMoveItem(payload).send();
}
Future<Either<OptionalRow, FlowyError>> getRow() {
final payload = GridRowIdPayload.create()
Future<Either<OptionalRowPB, FlowyError>> getRow() {
final payload = GridRowIdPayloadPB.create()
..gridId = gridId
..blockId = blockId
..rowId = rowId;
@ -290,7 +306,7 @@ class RowService {
}
Future<Either<Unit, FlowyError>> deleteRow() {
final payload = GridRowIdPayload.create()
final payload = GridRowIdPayloadPB.create()
..gridId = gridId
..blockId = blockId
..rowId = rowId;
@ -299,7 +315,7 @@ class RowService {
}
Future<Either<Unit, FlowyError>> duplicateRow() {
final payload = GridRowIdPayload.create()
final payload = GridRowIdPayloadPB.create()
..gridId = gridId
..blockId = blockId
..rowId = rowId;
@ -309,15 +325,15 @@ class RowService {
}
@freezed
class GridRow with _$GridRow {
const factory GridRow({
class GridRowInfo with _$GridRowInfo {
const factory GridRowInfo({
required String gridId,
required String blockId,
required String rowId,
required UnmodifiableListView<Field> fields,
required String id,
required UnmodifiableListView<GridFieldPB> fields,
required double height,
Row? data,
}) = _GridRow;
GridRowPB? rawRow,
}) = _GridRowInfo;
}
typedef InsertedIndexs = List<InsertedIndex>;
@ -344,7 +360,7 @@ class InsertedIndex {
class DeletedIndex {
final int index;
final GridRow row;
final GridRowInfo row;
DeletedIndex({
required this.index,
required this.row,

View File

@ -10,7 +10,7 @@ part 'property_bloc.freezed.dart';
class GridPropertyBloc extends Bloc<GridPropertyEvent, GridPropertyState> {
final GridFieldCache _fieldCache;
Function(List<Field>)? _onFieldsFn;
Function(List<GridFieldPB>)? _onFieldsFn;
GridPropertyBloc({required String gridId, required GridFieldCache fieldCache})
: _fieldCache = fieldCache,
@ -62,7 +62,7 @@ class GridPropertyBloc extends Bloc<GridPropertyEvent, GridPropertyState> {
class GridPropertyEvent with _$GridPropertyEvent {
const factory GridPropertyEvent.initial() = _Initial;
const factory GridPropertyEvent.setFieldVisibility(String fieldId, bool visibility) = _SetFieldVisibility;
const factory GridPropertyEvent.didReceiveFieldUpdate(List<Field> fields) = _DidReceiveFieldUpdate;
const factory GridPropertyEvent.didReceiveFieldUpdate(List<GridFieldPB> fields) = _DidReceiveFieldUpdate;
const factory GridPropertyEvent.moveField(int fromIndex, int toIndex) = _MoveField;
}
@ -70,10 +70,10 @@ class GridPropertyEvent with _$GridPropertyEvent {
class GridPropertyState with _$GridPropertyState {
const factory GridPropertyState({
required String gridId,
required List<Field> fields,
required List<GridFieldPB> fields,
}) = _GridPropertyState;
factory GridPropertyState.initial(String gridId, List<Field> fields) => GridPropertyState(
factory GridPropertyState.initial(String gridId, List<GridFieldPB> fields) => GridPropertyState(
gridId: gridId,
fields: fields,
);

View File

@ -3,7 +3,7 @@ import 'package:app_flowy/workspace/application/edit_pannel/edit_context.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/workspace.pb.dart' show CurrentWorkspaceSetting;
import 'package:flowy_sdk/protobuf/flowy-folder/workspace.pb.dart' show CurrentWorkspaceSettingPB;
import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@ -13,45 +13,51 @@ part 'home_bloc.freezed.dart';
class HomeBloc extends Bloc<HomeEvent, HomeState> {
final UserWorkspaceListener _listener;
HomeBloc(UserProfile user, CurrentWorkspaceSetting workspaceSetting)
HomeBloc(UserProfilePB user, CurrentWorkspaceSettingPB workspaceSetting)
: _listener = UserWorkspaceListener(userProfile: user),
super(HomeState.initial(workspaceSetting)) {
on<HomeEvent>((event, emit) async {
await event.map(
initial: (_Initial value) {
_listener.start(
onAuthChanged: (result) => _authDidChanged(result),
onSettingUpdated: (result) {
result.fold(
(setting) => add(HomeEvent.didReceiveWorkspaceSetting(setting)),
(r) => Log.error(r),
);
},
);
},
showLoading: (e) async {
emit(state.copyWith(isLoading: e.isLoading));
},
setEditPannel: (e) async {
emit(state.copyWith(pannelContext: some(e.editContext)));
},
dismissEditPannel: (value) async {
emit(state.copyWith(pannelContext: none()));
},
forceCollapse: (e) async {
emit(state.copyWith(forceCollapse: e.forceCollapse));
},
didReceiveWorkspaceSetting: (_DidReceiveWorkspaceSetting value) {
emit(state.copyWith(workspaceSetting: value.setting));
},
unauthorized: (_Unauthorized value) {
emit(state.copyWith(unauthorized: true));
},
collapseMenu: (e) {
emit(state.copyWith(isMenuCollapsed: !state.isMenuCollapsed));
},
);
});
on<HomeEvent>(
(event, emit) async {
await event.map(
initial: (_Initial value) {
_listener.start(
onAuthChanged: (result) => _authDidChanged(result),
onSettingUpdated: (result) {
result.fold(
(setting) => add(HomeEvent.didReceiveWorkspaceSetting(setting)),
(r) => Log.error(r),
);
},
);
},
showLoading: (e) async {
emit(state.copyWith(isLoading: e.isLoading));
},
setEditPannel: (e) async {
emit(state.copyWith(pannelContext: some(e.editContext)));
},
dismissEditPannel: (value) async {
emit(state.copyWith(pannelContext: none()));
},
forceCollapse: (e) async {
emit(state.copyWith(forceCollapse: e.forceCollapse));
},
didReceiveWorkspaceSetting: (_DidReceiveWorkspaceSetting value) {
emit(state.copyWith(workspaceSetting: value.setting));
},
unauthorized: (_Unauthorized value) {
emit(state.copyWith(unauthorized: true));
},
collapseMenu: (e) {
emit(state.copyWith(isMenuCollapsed: !state.isMenuCollapsed));
},
editPannelResized: (e) {
final newOffset = (state.resizeOffset + e.offset).clamp(-50, 200).toDouble();
emit(state.copyWith(resizeOffset: newOffset));
},
);
},
);
}
@override
@ -76,9 +82,10 @@ class HomeEvent with _$HomeEvent {
const factory HomeEvent.forceCollapse(bool forceCollapse) = _ForceCollapse;
const factory HomeEvent.setEditPannel(EditPannelContext editContext) = _ShowEditPannel;
const factory HomeEvent.dismissEditPannel() = _DismissEditPannel;
const factory HomeEvent.didReceiveWorkspaceSetting(CurrentWorkspaceSetting setting) = _DidReceiveWorkspaceSetting;
const factory HomeEvent.didReceiveWorkspaceSetting(CurrentWorkspaceSettingPB setting) = _DidReceiveWorkspaceSetting;
const factory HomeEvent.unauthorized(String msg) = _Unauthorized;
const factory HomeEvent.collapseMenu() = _CollapseMenu;
const factory HomeEvent.editPannelResized(double offset) = _EditPannelResized;
}
@freezed
@ -87,17 +94,19 @@ class HomeState with _$HomeState {
required bool isLoading,
required bool forceCollapse,
required Option<EditPannelContext> pannelContext,
required CurrentWorkspaceSetting workspaceSetting,
required CurrentWorkspaceSettingPB workspaceSetting,
required bool unauthorized,
required bool isMenuCollapsed,
required double resizeOffset,
}) = _HomeState;
factory HomeState.initial(CurrentWorkspaceSetting workspaceSetting) => HomeState(
factory HomeState.initial(CurrentWorkspaceSettingPB workspaceSetting) => HomeState(
isLoading: false,
forceCollapse: false,
pannelContext: none(),
workspaceSetting: workspaceSetting,
unauthorized: false,
isMenuCollapsed: false,
resizeOffset: 0,
);
}

View File

@ -41,7 +41,7 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> {
if (state.apps.length > value.fromIndex) {
final app = state.apps[value.fromIndex];
_workspaceService.moveApp(appId: app.id, fromIndex: value.fromIndex, toIndex: value.toIndex);
final apps = List<App>.from(state.apps);
final apps = List<AppPB>.from(state.apps);
apps.insert(value.toIndex, apps.removeAt(value.fromIndex));
emit(state.copyWith(apps: apps));
}
@ -79,7 +79,7 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> {
));
}
void _handleAppsOrFail(Either<List<App>, FlowyError> appsOrFail) {
void _handleAppsOrFail(Either<List<AppPB>, FlowyError> appsOrFail) {
appsOrFail.fold(
(apps) => add(MenuEvent.didReceiveApps(left(apps))),
(error) => add(MenuEvent.didReceiveApps(right(error))),
@ -93,13 +93,13 @@ class MenuEvent with _$MenuEvent {
const factory MenuEvent.openPage(Plugin plugin) = _OpenPage;
const factory MenuEvent.createApp(String name, {String? desc}) = _CreateApp;
const factory MenuEvent.moveApp(int fromIndex, int toIndex) = _MoveApp;
const factory MenuEvent.didReceiveApps(Either<List<App>, FlowyError> appsOrFail) = _ReceiveApps;
const factory MenuEvent.didReceiveApps(Either<List<AppPB>, FlowyError> appsOrFail) = _ReceiveApps;
}
@freezed
class MenuState with _$MenuState {
const factory MenuState({
required List<App> apps,
required List<AppPB> apps,
required Either<Unit, FlowyError> successOrFailure,
required Plugin plugin,
}) = _MenuState;

View File

@ -14,7 +14,7 @@ class MenuUserBloc extends Bloc<MenuUserEvent, MenuUserState> {
final UserService _userService;
final UserListener _userListener;
final UserWorkspaceListener _userWorkspaceListener;
final UserProfile userProfile;
final UserProfilePB userProfile;
MenuUserBloc(this.userProfile)
: _userListener = UserListener(userProfile: userProfile),
@ -31,7 +31,7 @@ class MenuUserBloc extends Bloc<MenuUserEvent, MenuUserState> {
fetchWorkspaces: () async {
//
},
didReceiveUserProfile: (UserProfile newUserProfile) {
didReceiveUserProfile: (UserProfilePB newUserProfile) {
emit(state.copyWith(userProfile: newUserProfile));
},
);
@ -50,14 +50,14 @@ class MenuUserBloc extends Bloc<MenuUserEvent, MenuUserState> {
result.fold((l) => null, (error) => Log.error(error));
}
void _profileUpdated(Either<UserProfile, FlowyError> userProfileOrFailed) {
void _profileUpdated(Either<UserProfilePB, FlowyError> userProfileOrFailed) {
userProfileOrFailed.fold(
(newUserProfile) => add(MenuUserEvent.didReceiveUserProfile(newUserProfile)),
(err) => Log.error(err),
);
}
void _workspaceListUpdated(Either<List<Workspace>, FlowyError> workspacesOrFailed) {
void _workspaceListUpdated(Either<List<WorkspacePB>, FlowyError> workspacesOrFailed) {
// Do nothing by now
}
}
@ -66,18 +66,19 @@ class MenuUserBloc extends Bloc<MenuUserEvent, MenuUserState> {
class MenuUserEvent with _$MenuUserEvent {
const factory MenuUserEvent.initial() = _Initial;
const factory MenuUserEvent.fetchWorkspaces() = _FetchWorkspaces;
const factory MenuUserEvent.didReceiveUserProfile(UserProfile newUserProfile) = _DidReceiveUserProfile;
const factory MenuUserEvent.updateUserName(String name) = _UpdateUserName;
const factory MenuUserEvent.didReceiveUserProfile(UserProfilePB newUserProfile) = _DidReceiveUserProfile;
}
@freezed
class MenuUserState with _$MenuUserState {
const factory MenuUserState({
required UserProfile userProfile,
required Option<List<Workspace>> workspaces,
required UserProfilePB userProfile,
required Option<List<WorkspacePB>> workspaces,
required Either<Unit, String> successOrFailure,
}) = _MenuUserState;
factory MenuUserState.initial(UserProfile userProfile) => MenuUserState(
factory MenuUserState.initial(UserProfilePB userProfile) => MenuUserState(
userProfile: userProfile,
workspaces: none(),
successOrFailure: left(unit),

View File

@ -62,7 +62,7 @@ class ViewSectionBloc extends Bloc<ViewSectionEvent, ViewSectionState> {
Future<void> _moveView(_MoveView value, Emitter<ViewSectionState> emit) async {
if (value.fromIndex < state.views.length) {
final viewId = state.views[value.fromIndex].id;
final views = List<View>.from(state.views);
final views = List<ViewPB>.from(state.views);
views.insert(value.toIndex, views.removeAt(value.fromIndex));
emit(state.copyWith(views: views));
@ -92,16 +92,16 @@ class ViewSectionBloc extends Bloc<ViewSectionEvent, ViewSectionState> {
@freezed
class ViewSectionEvent with _$ViewSectionEvent {
const factory ViewSectionEvent.initial() = _Initial;
const factory ViewSectionEvent.setSelectedView(View? view) = _SetSelectedView;
const factory ViewSectionEvent.setSelectedView(ViewPB? view) = _SetSelectedView;
const factory ViewSectionEvent.moveView(int fromIndex, int toIndex) = _MoveView;
const factory ViewSectionEvent.didReceiveViewUpdated(List<View> views) = _DidReceiveViewUpdated;
const factory ViewSectionEvent.didReceiveViewUpdated(List<ViewPB> views) = _DidReceiveViewUpdated;
}
@freezed
class ViewSectionState with _$ViewSectionState {
const factory ViewSectionState({
required List<View> views,
View? selectedView,
required List<ViewPB> views,
ViewPB? selectedView,
}) = _ViewSectionState;
factory ViewSectionState.initial(AppViewDataContext appViewData) => ViewSectionState(

View File

@ -45,7 +45,7 @@ class TrashBloc extends Bloc<TrashEvent, TrashState> {
));
}
void _listenTrashUpdated(Either<List<Trash>, FlowyError> trashOrFailed) {
void _listenTrashUpdated(Either<List<TrashPB>, FlowyError> trashOrFailed) {
trashOrFailed.fold(
(trash) {
add(TrashEvent.didReceiveTrash(trash));
@ -66,9 +66,9 @@ class TrashBloc extends Bloc<TrashEvent, TrashState> {
@freezed
class TrashEvent with _$TrashEvent {
const factory TrashEvent.initial() = Initial;
const factory TrashEvent.didReceiveTrash(List<Trash> trash) = ReceiveTrash;
const factory TrashEvent.didReceiveTrash(List<TrashPB> trash) = ReceiveTrash;
const factory TrashEvent.putback(String trashId) = Putback;
const factory TrashEvent.delete(Trash trash) = Delete;
const factory TrashEvent.delete(TrashPB trash) = Delete;
const factory TrashEvent.restoreAll() = RestoreAll;
const factory TrashEvent.deleteAll() = DeleteAll;
}
@ -76,7 +76,7 @@ class TrashEvent with _$TrashEvent {
@freezed
class TrashState with _$TrashState {
const factory TrashState({
required List<Trash> objects,
required List<TrashPB> objects,
required Either<Unit, FlowyError> successOrFailure,
}) = _TrashState;

View File

@ -8,7 +8,7 @@ import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/trash.pb.dart';
import 'package:flowy_sdk/rust_stream.dart';
typedef TrashUpdatedCallback = void Function(Either<List<Trash>, FlowyError> trashOrFailed);
typedef TrashUpdatedCallback = void Function(Either<List<TrashPB>, FlowyError> trashOrFailed);
class TrashListener {
StreamSubscription<SubscribeObject>? _subscription;
@ -27,7 +27,7 @@ class TrashListener {
if (_trashUpdated != null) {
result.fold(
(payload) {
final repeatedTrash = RepeatedTrash.fromBuffer(payload);
final repeatedTrash = RepeatedTrashPB.fromBuffer(payload);
_trashUpdated!(left(repeatedTrash.items));
},
(error) => _trashUpdated!(right(error)),

View File

@ -5,24 +5,24 @@ import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/trash.pb.dart';
class TrashService {
Future<Either<RepeatedTrash, FlowyError>> readTrash() {
Future<Either<RepeatedTrashPB, FlowyError>> readTrash() {
return FolderEventReadTrash().send();
}
Future<Either<Unit, FlowyError>> putback(String trashId) {
final id = TrashId.create()..id = trashId;
final id = TrashIdPB.create()..id = trashId;
return FolderEventPutbackTrash(id).send();
}
Future<Either<Unit, FlowyError>> deleteViews(List<Tuple2<String, TrashType>> trashList) {
final items = trashList.map((trash) {
return TrashId.create()
return TrashIdPB.create()
..id = trash.value1
..ty = trash.value2;
});
final ids = RepeatedTrashId(items: items);
final ids = RepeatedTrashIdPB(items: items);
return FolderEventDeleteTrash(ids).send();
}

View File

@ -11,7 +11,7 @@ part 'view_bloc.freezed.dart';
class ViewBloc extends Bloc<ViewEvent, ViewState> {
final ViewService service;
final ViewListener listener;
final View view;
final ViewPB view;
ViewBloc({
required this.view,
@ -81,18 +81,18 @@ class ViewEvent with _$ViewEvent {
const factory ViewEvent.rename(String newName) = Rename;
const factory ViewEvent.delete() = Delete;
const factory ViewEvent.duplicate() = Duplicate;
const factory ViewEvent.viewDidUpdate(Either<View, FlowyError> result) = ViewDidUpdate;
const factory ViewEvent.viewDidUpdate(Either<ViewPB, FlowyError> result) = ViewDidUpdate;
}
@freezed
class ViewState with _$ViewState {
const factory ViewState({
required View view,
required ViewPB view,
required bool isEditing,
required Either<Unit, FlowyError> successOrFailure,
}) = _ViewState;
factory ViewState.init(View view) => ViewState(
factory ViewState.init(ViewPB view) => ViewState(
view: view,
isEditing: false,
successOrFailure: left(unit),

View File

@ -32,7 +32,7 @@ extension FlowyPluginExtension on FlowyPlugin {
}
}
extension ViewExtension on View {
extension ViewExtension on ViewPB {
Widget renderThumbnail({Color? iconColor}) {
String thumbnail = "file_icon";

View File

@ -9,9 +9,9 @@ import 'package:flowy_sdk/protobuf/flowy-folder/dart_notification.pb.dart';
import 'package:flowy_sdk/rust_stream.dart';
import 'package:flowy_infra/notifier.dart';
typedef DeleteViewNotifyValue = Either<View, FlowyError>;
typedef UpdateViewNotifiedValue = Either<View, FlowyError>;
typedef RestoreViewNotifiedValue = Either<View, FlowyError>;
typedef DeleteViewNotifyValue = Either<ViewPB, FlowyError>;
typedef UpdateViewNotifiedValue = Either<ViewPB, FlowyError>;
typedef RestoreViewNotifiedValue = Either<ViewPB, FlowyError>;
class ViewListener {
StreamSubscription<SubscribeObject>? _subscription;
@ -19,7 +19,7 @@ class ViewListener {
final PublishNotifier<DeleteViewNotifyValue> _deletedNotifier = PublishNotifier();
final PublishNotifier<RestoreViewNotifiedValue> _restoredNotifier = PublishNotifier();
FolderNotificationParser? _parser;
View view;
ViewPB view;
ViewListener({
required this.view,
@ -62,19 +62,19 @@ class ViewListener {
switch (ty) {
case FolderNotification.ViewUpdated:
result.fold(
(payload) => _updatedViewNotifier.value = left(View.fromBuffer(payload)),
(payload) => _updatedViewNotifier.value = left(ViewPB.fromBuffer(payload)),
(error) => _updatedViewNotifier.value = right(error),
);
break;
case FolderNotification.ViewDeleted:
result.fold(
(payload) => _deletedNotifier.value = left(View.fromBuffer(payload)),
(payload) => _deletedNotifier.value = left(ViewPB.fromBuffer(payload)),
(error) => _deletedNotifier.value = right(error),
);
break;
case FolderNotification.ViewRestored:
result.fold(
(payload) => _restoredNotifier.value = left(View.fromBuffer(payload)),
(payload) => _restoredNotifier.value = left(ViewPB.fromBuffer(payload)),
(error) => _restoredNotifier.value = right(error),
);
break;

View File

@ -5,13 +5,13 @@ import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
class ViewService {
Future<Either<View, FlowyError>> readView({required String viewId}) {
final request = ViewId(value: viewId);
Future<Either<ViewPB, FlowyError>> readView({required String viewId}) {
final request = ViewIdPB(value: viewId);
return FolderEventReadView(request).send();
}
Future<Either<View, FlowyError>> updateView({required String viewId, String? name, String? desc}) {
final request = UpdateViewPayload.create()..viewId = viewId;
Future<Either<ViewPB, FlowyError>> updateView({required String viewId, String? name, String? desc}) {
final request = UpdateViewPayloadPB.create()..viewId = viewId;
if (name != null) {
request.name = name;
@ -25,12 +25,12 @@ class ViewService {
}
Future<Either<Unit, FlowyError>> delete({required String viewId}) {
final request = RepeatedViewId.create()..items.add(viewId);
final request = RepeatedViewIdPB.create()..items.add(viewId);
return FolderEventDeleteView(request).send();
}
Future<Either<Unit, FlowyError>> duplicate({required String viewId}) {
final request = ViewId(value: viewId);
final request = ViewIdPB(value: viewId);
return FolderEventDuplicateView(request).send();
}
}

View File

@ -52,7 +52,7 @@ class WelcomeBloc extends Bloc<WelcomeEvent, WelcomeState> {
));
}
Future<void> _openWorkspace(Workspace workspace, Emitter<WelcomeState> emit) async {
Future<void> _openWorkspace(WorkspacePB workspace, Emitter<WelcomeState> emit) async {
final result = await userService.openWorkspace(workspace.id);
emit(result.fold(
(workspaces) => state.copyWith(successOrFailure: left(unit)),
@ -82,8 +82,8 @@ class WelcomeEvent with _$WelcomeEvent {
const factory WelcomeEvent.initial() = Initial;
// const factory WelcomeEvent.fetchWorkspaces() = FetchWorkspace;
const factory WelcomeEvent.createWorkspace(String name, String desc) = CreateWorkspace;
const factory WelcomeEvent.openWorkspace(Workspace workspace) = OpenWorkspace;
const factory WelcomeEvent.workspacesReveived(Either<List<Workspace>, FlowyError> workspacesOrFail) =
const factory WelcomeEvent.openWorkspace(WorkspacePB workspace) = OpenWorkspace;
const factory WelcomeEvent.workspacesReveived(Either<List<WorkspacePB>, FlowyError> workspacesOrFail) =
WorkspacesReceived;
}
@ -91,7 +91,7 @@ class WelcomeEvent with _$WelcomeEvent {
class WelcomeState with _$WelcomeState {
const factory WelcomeState({
required bool isLoading,
required List<Workspace> workspaces,
required List<WorkspacePB> workspaces,
required Either<Unit, FlowyError> successOrFailure,
}) = _WelcomeState;

View File

@ -3,21 +3,21 @@ import 'dart:typed_data';
import 'package:app_flowy/core/folder_notification.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_infra/notifier.dart';
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfile;
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfilePB;
import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/workspace.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/dart_notification.pb.dart';
typedef AppListNotifyValue = Either<List<App>, FlowyError>;
typedef WorkspaceNotifyValue = Either<Workspace, FlowyError>;
typedef AppListNotifyValue = Either<List<AppPB>, FlowyError>;
typedef WorkspaceNotifyValue = Either<WorkspacePB, FlowyError>;
class WorkspaceListener {
PublishNotifier<AppListNotifyValue>? _appsChangedNotifier = PublishNotifier();
PublishNotifier<WorkspaceNotifyValue>? _workspaceUpdatedNotifier = PublishNotifier();
FolderNotificationListener? _listener;
final UserProfile user;
final UserProfilePB user;
final String workspaceId;
WorkspaceListener({
@ -47,13 +47,13 @@ class WorkspaceListener {
switch (ty) {
case FolderNotification.WorkspaceUpdated:
result.fold(
(payload) => _workspaceUpdatedNotifier?.value = left(Workspace.fromBuffer(payload)),
(payload) => _workspaceUpdatedNotifier?.value = left(WorkspacePB.fromBuffer(payload)),
(error) => _workspaceUpdatedNotifier?.value = right(error),
);
break;
case FolderNotification.WorkspaceAppsChanged:
result.fold(
(payload) => _appsChangedNotifier?.value = left(RepeatedApp.fromBuffer(payload).items),
(payload) => _appsChangedNotifier?.value = left(RepeatedAppPB.fromBuffer(payload).items),
(error) => _appsChangedNotifier?.value = right(error),
);
break;

View File

@ -5,7 +5,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart' show MoveFolderItemPayload, MoveFolderItemType;
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart' show MoveFolderItemPayloadPB, MoveFolderItemType;
import 'package:flowy_sdk/protobuf/flowy-folder/workspace.pb.dart';
import 'package:app_flowy/generated/locale_keys.g.dart';
@ -15,16 +15,16 @@ class WorkspaceService {
WorkspaceService({
required this.workspaceId,
});
Future<Either<App, FlowyError>> createApp({required String name, required String desc}) {
final payload = CreateAppPayload.create()
Future<Either<AppPB, FlowyError>> createApp({required String name, required String desc}) {
final payload = CreateAppPayloadPB.create()
..name = name
..workspaceId = workspaceId
..desc = desc;
return FolderEventCreateApp(payload).send();
}
Future<Either<Workspace, FlowyError>> getWorkspace() {
final payload = WorkspaceId.create()..value = workspaceId;
Future<Either<WorkspacePB, FlowyError>> getWorkspace() {
final payload = WorkspaceIdPB.create()..value = workspaceId;
return FolderEventReadWorkspaces(payload).send().then((result) {
return result.fold(
(workspaces) {
@ -41,8 +41,8 @@ class WorkspaceService {
});
}
Future<Either<List<App>, FlowyError>> getApps() {
final payload = WorkspaceId.create()..value = workspaceId;
Future<Either<List<AppPB>, FlowyError>> getApps() {
final payload = WorkspaceIdPB.create()..value = workspaceId;
return FolderEventReadWorkspaceApps(payload).send().then((result) {
return result.fold(
(apps) => left(apps.items),
@ -56,7 +56,7 @@ class WorkspaceService {
required int fromIndex,
required int toIndex,
}) {
final payload = MoveFolderItemPayload.create()
final payload = MoveFolderItemPayloadPB.create()
..itemId = appId
..from = fromIndex
..to = toIndex

View File

@ -27,6 +27,8 @@ class HomeLayout {
menuWidth = Sizes.sideBarLg;
}
menuWidth += homeBlocState.resizeOffset;
if (forceCollapse) {
showMenu = false;
} else {

View File

@ -5,8 +5,9 @@ import 'package:app_flowy/workspace/presentation/widgets/float_bubble/question_b
import 'package:app_flowy/startup/startup.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_infra_ui/style_widget/container.dart';
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfile;
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfilePB;
import 'package:flowy_sdk/protobuf/flowy-folder/protobuf.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:styled_widget/styled_widget.dart';
@ -18,8 +19,8 @@ import 'home_stack.dart';
import 'menu/menu.dart';
class HomeScreen extends StatefulWidget {
final UserProfile user;
final CurrentWorkspaceSetting workspaceSetting;
final UserProfilePB user;
final CurrentWorkspaceSettingPB workspaceSetting;
const HomeScreen(this.user, this.workspaceSetting, {Key? key}) : super(key: key);
@override
@ -27,7 +28,7 @@ class HomeScreen extends StatefulWidget {
}
class _HomeScreenState extends State<HomeScreen> {
View? initialView;
ViewPB? initialView;
@override
void initState() {
@ -87,6 +88,7 @@ class _HomeScreenState extends State<HomeScreen> {
context: context,
state: state,
);
final homeMenuResizer = _buildHomeMenuResizer(context: context);
final editPannel = _buildEditPannel(
homeState: state,
layout: layout,
@ -99,6 +101,7 @@ class _HomeScreenState extends State<HomeScreen> {
homeMenu: menu,
editPannel: editPannel,
bubble: bubble,
homeMenuResizer: homeMenuResizer,
);
},
);
@ -122,7 +125,10 @@ class _HomeScreenState extends State<HomeScreen> {
);
final latestView = workspaceSetting.hasLatestView() ? workspaceSetting.latestView : null;
getIt<MenuSharedState>().latestOpenView = latestView;
if (getIt<MenuSharedState>().latestOpenView == null) {
/// AppFlowy will open the view that the last time the user opened it. The _buildHomeMenu will get called when AppFlowy's screen resizes. So we only set the latestOpenView when it's null.
getIt<MenuSharedState>().latestOpenView = latestView;
}
return FocusTraversalGroup(child: RepaintBoundary(child: homeMenu));
}
@ -147,12 +153,31 @@ class _HomeScreenState extends State<HomeScreen> {
);
}
Widget _buildHomeMenuResizer({
required BuildContext context,
}) {
return MouseRegion(
cursor: SystemMouseCursors.resizeLeftRight,
child: GestureDetector(
dragStartBehavior: DragStartBehavior.down,
onPanUpdate: ((details) {
context.read<HomeBloc>().add(HomeEvent.editPannelResized(details.delta.dx));
}),
behavior: HitTestBehavior.translucent,
child: SizedBox(
width: 10,
height: MediaQuery.of(context).size.height,
)),
);
}
Widget _layoutWidgets({
required HomeLayout layout,
required Widget homeMenu,
required Widget homeStack,
required Widget editPannel,
required Widget bubble,
required Widget homeMenuResizer,
}) {
return Stack(
children: [
@ -167,6 +192,7 @@ class _HomeScreenState extends State<HomeScreen> {
.constrained(minWidth: 500)
.positioned(left: layout.homePageLOffset, right: layout.homePageROffset, bottom: 0, top: 0, animate: true)
.animate(layout.animDuration, Curves.easeOut),
homeMenuResizer.positioned(left: layout.homePageLOffset - 5),
bubble
.positioned(
right: 20,

View File

@ -19,7 +19,7 @@ import 'add_button.dart';
import 'right_click_action.dart';
class MenuAppHeader extends StatelessWidget {
final App app;
final AppPB app;
const MenuAppHeader(
this.app, {
Key? key,
@ -85,7 +85,7 @@ class MenuAppHeader extends StatelessWidget {
anchorDirection: AnchorDirection.bottomWithCenterAligned,
);
},
child: BlocSelector<AppBloc, AppState, App>(
child: BlocSelector<AppBloc, AppState, AppPB>(
selector: (state) => state.app,
builder: (context, app) => FlowyText.medium(
app.name,

View File

@ -10,7 +10,7 @@ import 'package:provider/provider.dart';
import 'section/section.dart';
class MenuApp extends StatefulWidget {
final App app;
final AppPB app;
const MenuApp(this.app, {Key? key}) : super(key: key);
@override

View File

@ -21,8 +21,8 @@ import 'disclosure_action.dart';
// ignore: must_be_immutable
class ViewSectionItem extends StatelessWidget {
final bool isSelected;
final View view;
final void Function(View) onSelected;
final ViewPB view;
final void Function(ViewPB) onSelected;
ViewSectionItem({
Key? key,

Some files were not shown because too many files have changed in this diff Show More