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 #!/usr/bin/env bash
echo "Running local AppFlowy pre-commit hook."
#flutter format . #flutter format .
##https://gist.github.com/benmccallum/28e4f216d9d72f5965133e6c43aaff6e ##https://gist.github.com/benmccallum/28e4f216d9d72f5965133e6c43aaff6e
limit=$(( 1 * 2**20 )) # 1MB 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 file_too_large $filename $file_size
exit 1; exit 1;
fi fi
done done

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

@ -1,15 +1,20 @@
#!/usr/bin/env bash #!/usr/bin/env bash
echo "Running local AppFlowy pre-push hook."
if [[ `git status --porcelain` ]]; then 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.' 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 exit 1
fi fi
printf "\e[33;1m%s\e[0m\n" 'Running the Flutter analyzer' printf "\e[33;1m%s\e[0m\n" 'Running the Flutter analyzer'
flutter analyze flutter analyze
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
printf "\e[31;1m%s\e[0m\n" 'Flutter analyzer error' printf "\e[31;1m%s\e[0m\n" 'Flutter analyzer error'
exit 1 exit 1
fi fi
printf "\e[33;1m%s\e[0m\n" 'Finished running the Flutter analyzer' printf "\e[33;1m%s\e[0m\n" 'Finished running the Flutter analyzer'
printf "\e[33;1m%s\e[0m\n" 'Running unit tests' printf "\e[33;1m%s\e[0m\n" 'Running unit tests'

View file

@ -16,7 +16,7 @@ jobs:
os: [ubuntu-latest, macos-latest] os: [ubuntu-latest, macos-latest]
include: include:
- os: ubuntu-latest - os: ubuntu-latest
flutter_profile: development-linux-x86 flutter_profile: development-linux-x86_64
- os: macos-latest - os: macos-latest
flutter_profile: development-mac-x86_64 flutter_profile: development-mac-x86_64
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@ -82,4 +82,4 @@ jobs:
- name: Build - name: Build
working-directory: frontend working-directory: frontend
run: | 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 - name: Build FlowySDK
working-directory: frontend working-directory: frontend
run: | run: |
cargo make --profile development-linux-x86 flowy-sdk-dev cargo make --profile development-linux-x86_64 flowy-sdk-dev
- name: Code Generation - name: Code Generation
working-directory: frontend/app_flowy working-directory: frontend/app_flowy

View file

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

View file

@ -67,7 +67,7 @@ jobs:
working-directory: frontend working-directory: frontend
run: | run: |
flutter config --enable-linux-desktop 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 - name: Upload Release Asset
id: upload-release-asset id: upload-release-asset

View file

@ -30,7 +30,7 @@ jobs:
- name: Build FlowySDK - name: Build FlowySDK
working-directory: frontend working-directory: frontend
run: | 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 - run: rustup component add rustfmt
working-directory: frontend/rust-lib working-directory: frontend/rust-lib

6
.gitignore vendored
View file

@ -27,4 +27,8 @@ frontend/.vscode/*
!frontend/.vscode/tasks.json !frontend/.vscode/tasks.json
!frontend/.vscode/launch.json !frontend/.vscode/launch.json
!frontend/.vscode/extensions.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/env.toml" },
{ path = "scripts/makefile/flutter.toml" }, { path = "scripts/makefile/flutter.toml" },
{ path = "scripts/makefile/tool.toml" }, { path = "scripts/makefile/tool.toml" },
{ path = "scripts/makefile/githooks.toml" },
] ]
[config] [config]
@ -100,7 +101,7 @@ CRATE_TYPE = "cdylib"
SDK_EXT = "dll" SDK_EXT = "dll"
APP_ENVIRONMENT = "production" APP_ENVIRONMENT = "production"
[env.development-linux-x86] [env.development-linux-x86_64]
TARGET_OS = "linux" TARGET_OS = "linux"
RUST_COMPILE_TARGET = "x86_64-unknown-linux-gnu" RUST_COMPILE_TARGET = "x86_64-unknown-linux-gnu"
BUILD_FLAG = "debug" BUILD_FLAG = "debug"
@ -109,7 +110,7 @@ FLUTTER_OUTPUT_DIR = "Debug"
SDK_EXT = "so" SDK_EXT = "so"
LINUX_ARCH = "x64" LINUX_ARCH = "x64"
[env.production-linux-x86] [env.production-linux-x86_64]
BUILD_FLAG = "release" BUILD_FLAG = "release"
TARGET_OS = "linux" TARGET_OS = "linux"
RUST_COMPILE_TARGET = "x86_64-unknown-linux-gnu" RUST_COMPILE_TARGET = "x86_64-unknown-linux-gnu"

View file

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

View file

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

View file

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

View file

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

View file

@ -2,7 +2,7 @@ import 'package:app_flowy/user/application/auth_service.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.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-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:freezed_annotation/freezed_annotation.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
@ -69,7 +69,7 @@ class SignInState with _$SignInState {
required bool isSubmitting, required bool isSubmitting,
required Option<String> passwordError, required Option<String> passwordError,
required Option<String> emailError, required Option<String> emailError,
required Option<Either<UserProfile, FlowyError>> successOrFail, required Option<Either<UserProfilePB, FlowyError>> successOrFail,
}) = _SignInState; }) = _SignInState;
factory SignInState.initial() => 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:dartz/dartz.dart';
import 'package:easy_localization/easy_localization.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-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:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
@ -120,7 +120,7 @@ class SignUpState with _$SignUpState {
required Option<String> passwordError, required Option<String> passwordError,
required Option<String> repeatPasswordError, required Option<String> repeatPasswordError,
required Option<String> emailError, required Option<String> emailError,
required Option<Either<UserProfile, FlowyError>> successOrFail, required Option<Either<UserProfilePB, FlowyError>> successOrFail,
}) = _SignUpState; }) = _SignUpState;
factory SignUpState.initial() => 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/protobuf/flowy-user/dart_notification.pb.dart' as user;
import 'package:flowy_sdk/rust_stream.dart'; import 'package:flowy_sdk/rust_stream.dart';
typedef UserProfileNotifyValue = Either<UserProfile, FlowyError>; typedef UserProfileNotifyValue = Either<UserProfilePB, FlowyError>;
typedef AuthNotifyValue = Either<Unit, FlowyError>; typedef AuthNotifyValue = Either<Unit, FlowyError>;
class UserListener { class UserListener {
@ -22,9 +22,9 @@ class UserListener {
PublishNotifier<UserProfileNotifyValue>? _profileNotifier = PublishNotifier(); PublishNotifier<UserProfileNotifyValue>? _profileNotifier = PublishNotifier();
UserNotificationParser? _userParser; UserNotificationParser? _userParser;
final UserProfile _userProfile; final UserProfilePB _userProfile;
UserListener({ UserListener({
required UserProfile userProfile, required UserProfilePB userProfile,
}) : _userProfile = userProfile; }) : _userProfile = userProfile;
void start({ void start({
@ -65,7 +65,7 @@ class UserListener {
break; break;
case user.UserNotification.UserProfileUpdated: case user.UserNotification.UserProfileUpdated:
result.fold( result.fold(
(payload) => _profileNotifier?.value = left(UserProfile.fromBuffer(payload)), (payload) => _profileNotifier?.value = left(UserProfilePB.fromBuffer(payload)),
(error) => _profileNotifier?.value = right(error), (error) => _profileNotifier?.value = right(error),
); );
break; break;
@ -75,8 +75,8 @@ class UserListener {
} }
} }
typedef WorkspaceListNotifyValue = Either<List<Workspace>, FlowyError>; typedef WorkspaceListNotifyValue = Either<List<WorkspacePB>, FlowyError>;
typedef WorkspaceSettingNotifyValue = Either<CurrentWorkspaceSetting, FlowyError>; typedef WorkspaceSettingNotifyValue = Either<CurrentWorkspaceSettingPB, FlowyError>;
class UserWorkspaceListener { class UserWorkspaceListener {
PublishNotifier<AuthNotifyValue>? _authNotifier = PublishNotifier(); PublishNotifier<AuthNotifyValue>? _authNotifier = PublishNotifier();
@ -84,10 +84,10 @@ class UserWorkspaceListener {
PublishNotifier<WorkspaceSettingNotifyValue>? _settingChangedNotifier = PublishNotifier(); PublishNotifier<WorkspaceSettingNotifyValue>? _settingChangedNotifier = PublishNotifier();
FolderNotificationListener? _listener; FolderNotificationListener? _listener;
final UserProfile _userProfile; final UserProfilePB _userProfile;
UserWorkspaceListener({ UserWorkspaceListener({
required UserProfile userProfile, required UserProfilePB userProfile,
}) : _userProfile = userProfile; }) : _userProfile = userProfile;
void start({ void start({
@ -119,13 +119,13 @@ class UserWorkspaceListener {
case FolderNotification.UserDeleteWorkspace: case FolderNotification.UserDeleteWorkspace:
case FolderNotification.WorkspaceListUpdated: case FolderNotification.WorkspaceListUpdated:
result.fold( result.fold(
(payload) => _workspacesChangedNotifier?.value = left(RepeatedWorkspace.fromBuffer(payload).items), (payload) => _workspacesChangedNotifier?.value = left(RepeatedWorkspacePB.fromBuffer(payload).items),
(error) => _workspacesChangedNotifier?.value = right(error), (error) => _workspacesChangedNotifier?.value = right(error),
); );
break; break;
case FolderNotification.WorkspaceSetting: case FolderNotification.WorkspaceSetting:
result.fold( result.fold(
(payload) => _settingChangedNotifier?.value = left(CurrentWorkspaceSetting.fromBuffer(payload)), (payload) => _settingChangedNotifier?.value = left(CurrentWorkspaceSettingPB.fromBuffer(payload)),
(error) => _settingChangedNotifier?.value = right(error), (error) => _settingChangedNotifier?.value = right(error),
); );
break; break;

View file

@ -11,7 +11,7 @@ class UserService {
UserService({ UserService({
required this.userId, required this.userId,
}); });
Future<Either<UserProfile, FlowyError>> getUserProfile({required String userId}) { Future<Either<UserProfilePB, FlowyError>> getUserProfile({required String userId}) {
return UserEventGetUserProfile().send(); return UserEventGetUserProfile().send();
} }
@ -20,7 +20,7 @@ class UserService {
String? password, String? password,
String? email, String? email,
}) { }) {
var payload = UpdateUserProfilePayload.create()..id = userId; var payload = UpdateUserProfilePayloadPB.create()..id = userId;
if (name != null) { if (name != null) {
payload.name = name; payload.name = name;
@ -49,8 +49,8 @@ class UserService {
return UserEventInitUser().send(); return UserEventInitUser().send();
} }
Future<Either<List<Workspace>, FlowyError>> getWorkspaces() { Future<Either<List<WorkspacePB>, FlowyError>> getWorkspaces() {
final request = WorkspaceId.create(); final request = WorkspaceIdPB.create();
return FolderEventReadWorkspaces(request).send().then((result) { return FolderEventReadWorkspaces(request).send().then((result) {
return result.fold( return result.fold(
@ -60,8 +60,8 @@ class UserService {
}); });
} }
Future<Either<Workspace, FlowyError>> openWorkspace(String workspaceId) { Future<Either<WorkspacePB, FlowyError>> openWorkspace(String workspaceId) {
final request = WorkspaceId.create()..value = workspaceId; final request = WorkspaceIdPB.create()..value = workspaceId;
return FolderEventOpenWorkspace(request).send().then((result) { return FolderEventOpenWorkspace(request).send().then((result) {
return result.fold( return result.fold(
(workspace) => left(workspace), (workspace) => left(workspace),
@ -70,8 +70,8 @@ class UserService {
}); });
} }
Future<Either<Workspace, FlowyError>> createWorkspace(String name, String desc) { Future<Either<WorkspacePB, FlowyError>> createWorkspace(String name, String desc) {
final request = CreateWorkspacePayload.create() final request = CreateWorkspacePayloadPB.create()
..name = name ..name = name
..desc = desc; ..desc = desc;
return FolderEventCreateWorkspace(request).send().then((result) { 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'; import 'package:flowy_sdk/protobuf/flowy-user/user_setting.pb.dart';
class UserSettingsService { class UserSettingsService {
Future<AppearanceSettings> getAppearanceSettings() async { Future<AppearanceSettingsPB> getAppearanceSettings() async {
final result = await UserEventGetAppearanceSetting().send(); final result = await UserEventGetAppearanceSetting().send();
return result.fold( return result.fold(
(AppearanceSettings setting) { (AppearanceSettingsPB setting) {
return setting; return setting;
}, },
(error) { (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(); 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:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
part 'auth_state.freezed.dart'; part 'auth_state.freezed.dart';
@freezed @freezed
class AuthState with _$AuthState { 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.unauthenticated(FlowyError error) = Unauthenticated;
const factory AuthState.initial() = _Initial; 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:app_flowy/workspace/presentation/home/home_screen.dart';
import 'package:flowy_infra/time/duration.dart'; import 'package:flowy_infra/time/duration.dart';
import 'package:flowy_infra_ui/widget/route/animation.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:flowy_sdk/protobuf/flowy-folder/protobuf.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -16,7 +16,7 @@ class AuthRouter {
// TODO: implement showForgetPasswordScreen // TODO: implement showForgetPasswordScreen
} }
void pushWelcomeScreen(BuildContext context, UserProfile userProfile) { void pushWelcomeScreen(BuildContext context, UserProfilePB userProfile) {
getIt<SplashRoute>().pushWelcomeScreen(context, 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( Navigator.push(
context, context,
PageRoutes.fade(() => HomeScreen(profile, workspaceSetting), RouteDurations.slow.inMilliseconds * .001), PageRoutes.fade(() => HomeScreen(profile, workspaceSetting), RouteDurations.slow.inMilliseconds * .001),
@ -37,7 +37,7 @@ class AuthRouter {
} }
class SplashRoute { class SplashRoute {
Future<void> pushWelcomeScreen(BuildContext context, UserProfile userProfile) async { Future<void> pushWelcomeScreen(BuildContext context, UserProfilePB userProfile) async {
final screen = WelcomeScreen(userProfile: userProfile); final screen = WelcomeScreen(userProfile: userProfile);
final workspaceId = await Navigator.of(context).push( final workspaceId = await Navigator.of(context).push(
PageRoutes.fade( PageRoutes.fade(
@ -49,7 +49,7 @@ class SplashRoute {
pushHomeScreen(context, userProfile, workspaceId); pushHomeScreen(context, userProfile, workspaceId);
} }
void pushHomeScreen(BuildContext context, UserProfile userProfile, CurrentWorkspaceSetting workspaceSetting) { void pushHomeScreen(BuildContext context, UserProfilePB userProfile, CurrentWorkspaceSettingPB workspaceSetting) {
Navigator.push( Navigator.push(
context, context,
PageRoutes.fade(() => HomeScreen(userProfile, workspaceSetting), RouteDurations.slow.inMilliseconds * .001), 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/widget/spacing.dart';
import 'package:flowy_infra_ui/style_widget/snap_bar.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-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/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:dartz/dartz.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( result.fold(
(user) => router.pushWelcomeScreen(context, user), (user) => router.pushWelcomeScreen(context, user),
(error) => showSnapBar(context, error.msg), (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/rounded_input_field.dart';
import 'package:flowy_infra_ui/widget/spacing.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-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:flowy_infra_ui/style_widget/snap_bar.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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( result.fold(
(user) => router.pushWelcomeScreen(context, user), (user) => router.pushWelcomeScreen(context, user),
(error) => showSnapBar(context, error.msg), (error) => showSnapBar(context, error.msg),

View file

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

View file

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

View file

@ -18,7 +18,7 @@ import 'package:dartz/dartz.dart';
part 'app_bloc.freezed.dart'; part 'app_bloc.freezed.dart';
class AppBloc extends Bloc<AppEvent, AppState> { class AppBloc extends Bloc<AppEvent, AppState> {
final App app; final AppPB app;
final AppService appService; final AppService appService;
final AppListener appListener; final AppListener appListener;
@ -103,7 +103,7 @@ class AppBloc extends Bloc<AppEvent, AppState> {
return super.close(); 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; final latestCreatedView = state.latestCreatedView;
AppState newState = state.copyWith(views: views); AppState newState = state.copyWith(views: views);
if (latestCreatedView != null) { if (latestCreatedView != null) {
@ -139,20 +139,20 @@ class AppEvent with _$AppEvent {
) = CreateView; ) = CreateView;
const factory AppEvent.delete() = Delete; const factory AppEvent.delete() = Delete;
const factory AppEvent.rename(String newName) = Rename; const factory AppEvent.rename(String newName) = Rename;
const factory AppEvent.didReceiveViewUpdated(List<View> views) = ReceiveViews; const factory AppEvent.didReceiveViewUpdated(List<ViewPB> views) = ReceiveViews;
const factory AppEvent.appDidUpdate(App app) = AppDidUpdate; const factory AppEvent.appDidUpdate(AppPB app) = AppDidUpdate;
} }
@freezed @freezed
class AppState with _$AppState { class AppState with _$AppState {
const factory AppState({ const factory AppState({
required App app, required AppPB app,
required List<View> views, required List<ViewPB> views,
View? latestCreatedView, ViewPB? latestCreatedView,
required Either<Unit, FlowyError> successOrFailure, required Either<Unit, FlowyError> successOrFailure,
}) = _AppState; }) = _AppState;
factory AppState.initial(App app) => AppState( factory AppState.initial(AppPB app) => AppState(
app: app, app: app,
views: [], views: [],
successOrFailure: left(unit), successOrFailure: left(unit),
@ -161,8 +161,8 @@ class AppState with _$AppState {
class AppViewDataContext extends ChangeNotifier { class AppViewDataContext extends ChangeNotifier {
final String appId; final String appId;
final ValueNotifier<List<View>> _viewsNotifier = ValueNotifier([]); final ValueNotifier<List<ViewPB>> _viewsNotifier = ValueNotifier([]);
final ValueNotifier<View?> _selectedViewNotifier = ValueNotifier(null); final ValueNotifier<ViewPB?> _selectedViewNotifier = ValueNotifier(null);
VoidCallback? _menuSharedStateListener; VoidCallback? _menuSharedStateListener;
ExpandableController expandController = ExpandableController(initialExpanded: false); 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() { listener() {
callback(_selectedViewNotifier.value); callback(_selectedViewNotifier.value);
} }
@ -186,7 +186,7 @@ class AppViewDataContext extends ChangeNotifier {
_selectedViewNotifier.removeListener(listener); _selectedViewNotifier.removeListener(listener);
} }
void _setLatestView(View? view) { void _setLatestView(ViewPB? view) {
view?.freeze(); view?.freeze();
if (_selectedViewNotifier.value != view) { 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) { if (_viewsNotifier.value != views) {
_viewsNotifier.value = views; _viewsNotifier.value = views;
_expandIfNeed(); _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() { listener() {
callback(views); 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/protobuf/flowy-folder/dart_notification.pb.dart';
import 'package:flowy_sdk/rust_stream.dart'; import 'package:flowy_sdk/rust_stream.dart';
typedef AppDidUpdateCallback = void Function(App app); typedef AppDidUpdateCallback = void Function(AppPB app);
typedef ViewsDidChangeCallback = void Function(Either<List<View>, FlowyError> viewsOrFailed); typedef ViewsDidChangeCallback = void Function(Either<List<ViewPB>, FlowyError> viewsOrFailed);
class AppListener { class AppListener {
StreamSubscription<SubscribeObject>? _subscription; StreamSubscription<SubscribeObject>? _subscription;
@ -37,7 +37,7 @@ class AppListener {
if (_viewsChanged != null) { if (_viewsChanged != null) {
result.fold( result.fold(
(payload) { (payload) {
final repeatedView = RepeatedView.fromBuffer(payload); final repeatedView = RepeatedViewPB.fromBuffer(payload);
_viewsChanged!(left(repeatedView.items)); _viewsChanged!(left(repeatedView.items));
}, },
(error) => _viewsChanged!(right(error)), (error) => _viewsChanged!(right(error)),
@ -48,7 +48,7 @@ class AppListener {
if (_updated != null) { if (_updated != null) {
result.fold( result.fold(
(payload) { (payload) {
final app = App.fromBuffer(payload); final app = AppPB.fromBuffer(payload);
_updated!(app); _updated!(app);
}, },
(error) => Log.error(error), (error) => Log.error(error),

View file

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

View file

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

View file

@ -17,7 +17,7 @@ part 'doc_bloc.freezed.dart';
typedef FlutterQuillDocument = Document; typedef FlutterQuillDocument = Document;
class DocumentBloc extends Bloc<DocumentEvent, DocumentState> { class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
final View view; final ViewPB view;
final DocumentService service; final DocumentService service;
final ViewListener listener; 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'; import 'package:flowy_sdk/protobuf/flowy-sync/text_block.pb.dart';
class DocumentService { class DocumentService {
Future<Either<TextBlockDelta, FlowyError>> openDocument({ Future<Either<TextBlockDeltaPB, FlowyError>> openDocument({
required String docId, required String docId,
}) async { }) 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(); return TextBlockEventGetBlockData(payload).send();
} }
Future<Either<TextBlockDelta, FlowyError>> composeDelta({required String docId, required String data}) { Future<Either<TextBlockDeltaPB, FlowyError>> composeDelta({required String docId, required String data}) {
final payload = TextBlockDelta.create() final payload = TextBlockDeltaPB.create()
..blockId = docId ..blockId = docId
..deltaStr = data; ..deltaStr = data;
return TextBlockEventApplyDelta(payload).send(); return TextBlockEventApplyDelta(payload).send();
} }
Future<Either<Unit, FlowyError>> closeDocument({required String docId}) { Future<Either<Unit, FlowyError>> closeDocument({required String docId}) {
final request = ViewId(value: docId); final request = ViewIdPB(value: docId);
return FolderEventCloseView(request).send(); return FolderEventCloseView(request).send();
} }
} }

View file

@ -13,7 +13,7 @@ part 'share_bloc.freezed.dart';
class DocShareBloc extends Bloc<DocShareEvent, DocShareState> { class DocShareBloc extends Bloc<DocShareEvent, DocShareState> {
ShareService service; ShareService service;
View view; ViewPB view;
DocShareBloc({required this.view, required this.service}) : super(const DocShareState.initial()) { DocShareBloc({required this.view, required this.service}) : super(const DocShareState.initial()) {
on<DocShareEvent>((event, emit) async { on<DocShareEvent>((event, emit) async {
await event.map( 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); final result = deltaToMarkdown(value.data);
value.data = result; value.data = result;
writeFile(result); writeFile(result);
@ -73,5 +73,5 @@ class DocShareEvent with _$DocShareEvent {
class DocShareState with _$DocShareState { class DocShareState with _$DocShareState {
const factory DocShareState.initial() = _Initial; const factory DocShareState.initial() = _Initial;
const factory DocShareState.loading() = _Loading; 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'; import 'package:flowy_sdk/protobuf/flowy-text-block/protobuf.dart';
class ShareService { class ShareService {
Future<Either<ExportData, FlowyError>> export(String docId, ExportType type) { Future<Either<ExportDataPB, FlowyError>> export(String docId, ExportType type) {
final request = ExportPayload.create() final request = ExportPayloadPB.create()
..viewId = docId ..viewId = docId
..exportType = type; ..exportType = type;
return TextBlockEventExportDocument(request).send(); return TextBlockEventExportDocument(request).send();
} }
Future<Either<ExportData, FlowyError>> exportText(String docId) { Future<Either<ExportDataPB, FlowyError>> exportText(String docId) {
return export(docId, ExportType.Text); return export(docId, ExportType.Text);
} }
Future<Either<ExportData, FlowyError>> exportMarkdown(String docId) { Future<Either<ExportDataPB, FlowyError>> exportMarkdown(String docId) {
return export(docId, ExportType.Markdown); return export(docId, ExportType.Markdown);
} }
Future<Either<ExportData, FlowyError>> exportURL(String docId) { Future<Either<ExportDataPB, FlowyError>> exportURL(String docId) {
return export(docId, ExportType.Link); 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'; 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 String gridId;
final GridBlock block; final GridBlockPB block;
late GridRowCacheService _rowCache; late GridRowCache _rowCache;
late GridBlockListener _listener; late GridBlockListener _listener;
List<GridRow> get rows => _rowCache.rows; List<GridRowInfo> get rows => _rowCache.rows;
GridRowCacheService get rowCache => _rowCache; GridRowCache get rowCache => _rowCache;
GridBlockCacheService({ GridBlockCache({
required this.gridId, required this.gridId,
required this.block, required this.block,
required GridFieldCache fieldCache, required GridFieldCache fieldCache,
}) { }) {
_rowCache = GridRowCacheService( _rowCache = GridRowCache(
gridId: gridId, gridId: gridId,
block: block, block: block,
delegate: GridRowCacheDelegateImpl(fieldCache), notifier: GridRowCacheFieldNotifierImpl(fieldCache),
); );
_listener = GridBlockListener(blockId: block.id); _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/block_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.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 { class GridBlockListener {
final String blockId; final String blockId;
@ -33,7 +33,7 @@ class GridBlockListener {
switch (ty) { switch (ty) {
case GridNotification.DidUpdateGridBlock: case GridNotification.DidUpdateGridBlock:
result.fold( result.fold(
(payload) => _rowsUpdateNotifier?.value = left([GridRowsChangeset.fromBuffer(payload)]), (payload) => _rowsUpdateNotifier?.value = left([GridBlockChangesetPB.fromBuffer(payload)]),
(error) => _rowsUpdateNotifier?.value = right(error), (error) => _rowsUpdateNotifier?.value = right(error),
); );
break; 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 { abstract class IGridCellDataConfig {
// The cell data will reload if it receives the field's change notification. // The cell data will reload if it receives the field's change notification.
bool get reloadOnFieldChanged; 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 { abstract class IGridCellDataParser<T> {
@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> {
T? parserData(List<int> data); T? parserData(List<int> data);
} }
class GridCellDataLoader<T> extends IGridCellDataLoader<T> { class GridCellDataLoader<T> {
final CellService service = CellService(); final CellService service = CellService();
final GridCell gridCell; final GridCellIdentifier cellId;
final ICellDataParser<T> parser; final IGridCellDataParser<T> parser;
final bool reloadOnFieldChanged;
@override
final IGridCellDataConfig config;
GridCellDataLoader({ GridCellDataLoader({
required this.gridCell, required this.cellId,
required this.parser, required this.parser,
this.config = const GridCellDataConfig(), this.reloadOnFieldChanged = false,
}); });
@override
Future<T?> loadData() { Future<T?> loadData() {
final fut = service.getCell( final fut = service.getCell(cellId: cellId);
gridId: gridCell.gridId,
fieldId: gridCell.field.id,
rowId: gridCell.rowId,
);
return fut.then( return fut.then(
(result) => result.fold((Cell cell) { (result) => result.fold((GridCellPB cell) {
try { try {
return parser.parserData(cell.data); return parser.parserData(cell.data);
} catch (e, s) { } catch (e, s) {
@ -72,30 +40,7 @@ class GridCellDataLoader<T> extends IGridCellDataLoader<T> {
} }
} }
class SelectOptionCellDataLoader extends IGridCellDataLoader<SelectOptionCellData> { class StringCellDataParser implements IGridCellDataParser<String> {
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> {
@override @override
String? parserData(List<int> data) { String? parserData(List<int> data) {
final s = utf8.decode(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 @override
DateCellData? parserData(List<int> data) { DateCellDataPB? parserData(List<int> data) {
if (data.isEmpty) { if (data.isEmpty) {
return null; return null;
} }
return DateCellData.fromBuffer(data); return DateCellDataPB.fromBuffer(data);
} }
} }
class SelectOptionCellDataParser implements ICellDataParser<SelectOptionCellData> { class SelectOptionCellDataParser implements IGridCellDataParser<SelectOptionCellDataPB> {
@override @override
SelectOptionCellData? parserData(List<int> data) { SelectOptionCellDataPB? parserData(List<int> data) {
if (data.isEmpty) { if (data.isEmpty) {
return null; return null;
} }
return SelectOptionCellData.fromBuffer(data); return SelectOptionCellDataPB.fromBuffer(data);
} }
} }
class URLCellDataParser implements ICellDataParser<URLCellData> { class URLCellDataParser implements IGridCellDataParser<URLCellDataPB> {
@override @override
URLCellData? parserData(List<int> data) { URLCellDataPB? parserData(List<int> data) {
if (data.isEmpty) { if (data.isEmpty) {
return null; return null;
} }
return URLCellData.fromBuffer(data); return URLCellDataPB.fromBuffer(data);
} }
} }

View file

@ -1,25 +1,22 @@
part of 'cell_service.dart'; 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); Future<Option<FlowyError>> save(D data);
} }
class CellDataPersistence implements _GridCellDataPersistence<String> { class CellDataPersistence implements IGridCellDataPersistence<String> {
final GridCell gridCell; final GridCellIdentifier cellId;
CellDataPersistence({ CellDataPersistence({
required this.gridCell, required this.cellId,
}); });
final CellService _cellService = CellService(); final CellService _cellService = CellService();
@override @override
Future<Option<FlowyError>> save(String data) async { Future<Option<FlowyError>> save(String data) async {
final fut = _cellService.updateCell( final fut = _cellService.updateCell(cellId: cellId, data: data);
gridId: gridCell.gridId,
fieldId: gridCell.field.id,
rowId: gridCell.rowId,
data: data,
);
return fut.then((result) { return fut.then((result) {
return result.fold( return result.fold(
@ -35,15 +32,15 @@ class CalendarData with _$CalendarData {
const factory CalendarData({required DateTime date, String? time}) = _CalendarData; const factory CalendarData({required DateTime date, String? time}) = _CalendarData;
} }
class DateCellDataPersistence implements _GridCellDataPersistence<CalendarData> { class DateCellDataPersistence implements IGridCellDataPersistence<CalendarData> {
final GridCell gridCell; final GridCellIdentifier cellId;
DateCellDataPersistence({ DateCellDataPersistence({
required this.gridCell, required this.cellId,
}); });
@override @override
Future<Option<FlowyError>> save(CalendarData data) { 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(); final date = (data.date.millisecondsSinceEpoch ~/ 1000).toString();
payload.date = date; payload.date = date;
@ -61,9 +58,9 @@ class DateCellDataPersistence implements _GridCellDataPersistence<CalendarData>
} }
} }
CellIdentifierPayload _cellIdentifier(GridCell gridCell) { GridCellIdentifierPayloadPB _makeCellIdPayload(GridCellIdentifier cellId) {
return CellIdentifierPayload.create() return GridCellIdentifierPayloadPB.create()
..gridId = gridCell.gridId ..gridId = cellId.gridId
..fieldId = gridCell.field.id ..fieldId = cellId.fieldId
..rowId = gridCell.rowId; ..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:async';
import 'dart:collection'; import 'dart:collection';
import 'package:app_flowy/workspace/application/grid/grid_service.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart'; import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.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/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/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: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/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.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/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 'package:app_flowy/workspace/application/grid/field/field_service.dart';
import 'dart:convert' show utf8; 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_service.freezed.dart';
part 'cell_data_loader.dart'; part 'cell_data_loader.dart';
part 'context_builder.dart'; part 'context_builder.dart';
part 'cache.dart'; part 'cell_cache.dart';
part 'cell_data_persistence.dart'; part 'cell_data_persistence.dart';
// key: rowId // key: rowId
@ -29,44 +32,46 @@ class CellService {
CellService(); CellService();
Future<Either<void, FlowyError>> updateCell({ Future<Either<void, FlowyError>> updateCell({
required String gridId, required GridCellIdentifier cellId,
required String fieldId,
required String rowId,
required String data, required String data,
}) { }) {
final payload = CellChangeset.create() final payload = CellChangesetPB.create()
..gridId = gridId ..gridId = cellId.gridId
..fieldId = fieldId ..fieldId = cellId.fieldId
..rowId = rowId ..rowId = cellId.rowId
..cellContentChangeset = data; ..content = data;
return GridEventUpdateCell(payload).send(); return GridEventUpdateCell(payload).send();
} }
Future<Either<Cell, FlowyError>> getCell({ Future<Either<GridCellPB, FlowyError>> getCell({
required String gridId, required GridCellIdentifier cellId,
required String fieldId,
required String rowId,
}) { }) {
final payload = CellIdentifierPayload.create() final payload = GridCellIdentifierPayloadPB.create()
..gridId = gridId ..gridId = cellId.gridId
..fieldId = fieldId ..fieldId = cellId.fieldId
..rowId = rowId; ..rowId = cellId.rowId;
return GridEventGetCell(payload).send(); return GridEventGetCell(payload).send();
} }
} }
/// Id of the cell
/// We can locate the cell by using gridId + rowId + field.id.
@freezed @freezed
class GridCell with _$GridCell { class GridCellIdentifier with _$GridCellIdentifier {
const factory GridCell({ const factory GridCellIdentifier({
required String gridId, required String gridId,
required String rowId, required String rowId,
required Field field, required GridFieldPB field,
}) = _GridCell; }) = _GridCellIdentifier;
// ignore: unused_element // ignore: unused_element
const GridCell._(); const GridCellIdentifier._();
String cellId() { String get fieldId => field.id;
return rowId + field.id + "${field.fieldType}";
FieldType get fieldType => field.fieldType;
ValueKey key() {
return ValueKey(rowId + fieldId + "${field.fieldType}");
} }
} }

View file

@ -1,154 +1,183 @@
part of 'cell_service.dart'; part of 'cell_service.dart';
typedef GridCellContext = _GridCellContext<String, String>; typedef GridCellController = IGridCellController<String, String>;
typedef GridSelectOptionCellContext = _GridCellContext<SelectOptionCellData, String>; typedef GridSelectOptionCellController = IGridCellController<SelectOptionCellDataPB, String>;
typedef GridDateCellContext = _GridCellContext<DateCellData, CalendarData>; typedef GridDateCellController = IGridCellController<DateCellDataPB, CalendarData>;
typedef GridURLCellContext = _GridCellContext<URLCellData, String>; typedef GridURLCellController = IGridCellController<URLCellDataPB, String>;
class GridCellContextBuilder { class GridCellControllerBuilder {
final GridCellCacheService _cellCache; final GridCellIdentifier _cellId;
final GridCell _gridCell; final GridCellCache _cellCache;
GridCellContextBuilder({ final GridFieldCache _fieldCache;
required GridCellCacheService cellCache,
required GridCell gridCell, GridCellControllerBuilder({
required GridCellIdentifier cellId,
required GridCellCache cellCache,
required GridFieldCache fieldCache,
}) : _cellCache = cellCache, }) : _cellCache = cellCache,
_gridCell = gridCell; _fieldCache = fieldCache,
_cellId = cellId;
_GridCellContext build() { IGridCellController build() {
switch (_gridCell.field.fieldType) { final cellFieldNotifier = GridCellFieldNotifier(notifier: _GridFieldChangedNotifierImpl(_fieldCache));
switch (_cellId.fieldType) {
case FieldType.Checkbox: case FieldType.Checkbox:
final cellDataLoader = GridCellDataLoader( final cellDataLoader = GridCellDataLoader(
gridCell: _gridCell, cellId: _cellId,
parser: StringCellDataParser(), parser: StringCellDataParser(),
); );
return GridCellContext( return GridCellController(
gridCell: _gridCell, cellId: _cellId,
cellCache: _cellCache, cellCache: _cellCache,
cellDataLoader: cellDataLoader, cellDataLoader: cellDataLoader,
cellDataPersistence: CellDataPersistence(gridCell: _gridCell), fieldNotifier: cellFieldNotifier,
cellDataPersistence: CellDataPersistence(cellId: _cellId),
); );
case FieldType.DateTime: case FieldType.DateTime:
final cellDataLoader = GridCellDataLoader( final cellDataLoader = GridCellDataLoader(
gridCell: _gridCell, cellId: _cellId,
parser: DateCellDataParser(), parser: DateCellDataParser(),
config: const GridCellDataConfig(reloadOnFieldChanged: true), reloadOnFieldChanged: true,
); );
return GridDateCellContext( return GridDateCellController(
gridCell: _gridCell, cellId: _cellId,
cellCache: _cellCache, cellCache: _cellCache,
cellDataLoader: cellDataLoader, cellDataLoader: cellDataLoader,
cellDataPersistence: DateCellDataPersistence(gridCell: _gridCell), fieldNotifier: cellFieldNotifier,
cellDataPersistence: DateCellDataPersistence(cellId: _cellId),
); );
case FieldType.Number: case FieldType.Number:
final cellDataLoader = GridCellDataLoader( final cellDataLoader = GridCellDataLoader(
gridCell: _gridCell, cellId: _cellId,
parser: StringCellDataParser(), parser: StringCellDataParser(),
config: const GridCellDataConfig(reloadOnCellChanged: true, reloadOnFieldChanged: true), reloadOnFieldChanged: true,
); );
return GridCellContext( return GridCellController(
gridCell: _gridCell, cellId: _cellId,
cellCache: _cellCache, cellCache: _cellCache,
cellDataLoader: cellDataLoader, cellDataLoader: cellDataLoader,
cellDataPersistence: CellDataPersistence(gridCell: _gridCell), fieldNotifier: cellFieldNotifier,
cellDataPersistence: CellDataPersistence(cellId: _cellId),
); );
case FieldType.RichText: case FieldType.RichText:
final cellDataLoader = GridCellDataLoader( final cellDataLoader = GridCellDataLoader(
gridCell: _gridCell, cellId: _cellId,
parser: StringCellDataParser(), parser: StringCellDataParser(),
); );
return GridCellContext( return GridCellController(
gridCell: _gridCell, cellId: _cellId,
cellCache: _cellCache, cellCache: _cellCache,
cellDataLoader: cellDataLoader, cellDataLoader: cellDataLoader,
cellDataPersistence: CellDataPersistence(gridCell: _gridCell), fieldNotifier: cellFieldNotifier,
cellDataPersistence: CellDataPersistence(cellId: _cellId),
); );
case FieldType.MultiSelect: case FieldType.MultiSelect:
case FieldType.SingleSelect: case FieldType.SingleSelect:
final cellDataLoader = GridCellDataLoader( final cellDataLoader = GridCellDataLoader(
gridCell: _gridCell, cellId: _cellId,
parser: SelectOptionCellDataParser(), parser: SelectOptionCellDataParser(),
config: const GridCellDataConfig(reloadOnFieldChanged: true), reloadOnFieldChanged: true,
); );
return GridSelectOptionCellContext( return GridSelectOptionCellController(
gridCell: _gridCell, cellId: _cellId,
cellCache: _cellCache, cellCache: _cellCache,
cellDataLoader: cellDataLoader, cellDataLoader: cellDataLoader,
cellDataPersistence: CellDataPersistence(gridCell: _gridCell), fieldNotifier: cellFieldNotifier,
cellDataPersistence: CellDataPersistence(cellId: _cellId),
); );
case FieldType.URL: case FieldType.URL:
final cellDataLoader = GridCellDataLoader( final cellDataLoader = GridCellDataLoader(
gridCell: _gridCell, cellId: _cellId,
parser: URLCellDataParser(), parser: URLCellDataParser(),
); );
return GridURLCellContext( return GridURLCellController(
gridCell: _gridCell, cellId: _cellId,
cellCache: _cellCache, cellCache: _cellCache,
cellDataLoader: cellDataLoader, cellDataLoader: cellDataLoader,
cellDataPersistence: CellDataPersistence(gridCell: _gridCell), fieldNotifier: cellFieldNotifier,
cellDataPersistence: CellDataPersistence(cellId: _cellId),
); );
} }
throw UnimplementedError; throw UnimplementedError;
} }
} }
// T: the type of the CellData /// IGridCellController is used to manipulate the cell and receive notifications.
// D: the type of the data that will be save to disk /// * 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 // ignore: must_be_immutable
class _GridCellContext<T, D> extends Equatable { class IGridCellController<T, D> extends Equatable {
final GridCell gridCell; final GridCellIdentifier cellId;
final GridCellCacheService cellCache; final GridCellCache _cellsCache;
final _GridCellCacheKey _cacheKey; final GridCellCacheKey _cacheKey;
final IGridCellDataLoader<T> cellDataLoader;
final _GridCellDataPersistence<D> cellDataPersistence;
final FieldService _fieldService; final FieldService _fieldService;
final GridCellFieldNotifier _fieldNotifier;
final GridCellDataLoader<T> _cellDataLoader;
final IGridCellDataPersistence<D> _cellDataPersistence;
late final CellListener _cellListener; late final CellListener _cellListener;
late final ValueNotifier<T?>? _cellDataNotifier; ValueNotifier<T?>? _cellDataNotifier;
bool isListening = false; bool isListening = false;
VoidCallback? _onFieldChangedFn; VoidCallback? _onFieldChangedFn;
Timer? _loadDataOperation; Timer? _loadDataOperation;
Timer? _saveDataOperation; Timer? _saveDataOperation;
bool _isDispose = false;
_GridCellContext({ IGridCellController({
required this.gridCell, required this.cellId,
required this.cellCache, required GridCellCache cellCache,
required this.cellDataLoader, required GridCellFieldNotifier fieldNotifier,
required this.cellDataPersistence, required GridCellDataLoader<T> cellDataLoader,
}) : _fieldService = FieldService(gridId: gridCell.gridId, fieldId: gridCell.field.id), required IGridCellDataPersistence<D> cellDataPersistence,
_cacheKey = _GridCellCacheKey(rowId: gridCell.rowId, fieldId: gridCell.field.id); }) : _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() { IGridCellController<T, D> clone() {
return _GridCellContext( return IGridCellController(
gridCell: gridCell, cellId: cellId,
cellDataLoader: cellDataLoader, cellDataLoader: _cellDataLoader,
cellCache: cellCache, cellCache: _cellsCache,
cellDataPersistence: cellDataPersistence); 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? onCellFieldChanged}) {
VoidCallback? startListening({required void Function(T?) onCellChanged}) {
if (isListening) { if (isListening) {
Log.error("Already started. It seems like you should call clone first"); Log.error("Already started. It seems like you should call clone first");
return null; return null;
} }
isListening = true; 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) { _cellListener.start(onCellChanged: (result) {
result.fold( result.fold(
(_) => _loadData(), (_) => _loadData(),
@ -156,22 +185,27 @@ class _GridCellContext<T, D> extends Equatable {
); );
}); });
if (cellDataLoader.config.reloadOnFieldChanged) { /// 2.Listen on the field event and load the cell data if needed.
_onFieldChangedFn = () { _onFieldChangedFn = () {
_loadData(); if (onCellFieldChanged != null) {
}; onCellFieldChanged();
cellCache.addFieldListener(_cacheKey, _onFieldChangedFn!); }
}
onCellChangedFn() { /// reloadOnFieldChanged should be true if you need to load the data when the corresponding field is changed
onCellChanged(_cellDataNotifier?.value); /// For example:
/// 12 -> $12
if (cellDataLoader.config.reloadOnCellChanged) { if (_cellDataLoader.reloadOnFieldChanged) {
_loadData(); _loadData();
} }
} };
_fieldNotifier.register(_cacheKey, _onFieldChangedFn!);
/// Notify the listener, the cell data was changed.
onCellChangedFn() => onCellChanged(_cellDataNotifier?.value);
_cellDataNotifier?.addListener(onCellChangedFn); _cellDataNotifier?.addListener(onCellChangedFn);
// Return the function pointer that can be used when calling removeListener.
return onCellChangedFn; return onCellChangedFn;
} }
@ -179,29 +213,45 @@ class _GridCellContext<T, D> extends Equatable {
_cellDataNotifier?.removeListener(fn); _cellDataNotifier?.removeListener(fn);
} }
T? getCellData({bool loadIfNoCache = true}) { /// Return the cell data.
final data = cellCache.get(_cacheKey); /// The cell data will be read from the Cache first, and load from disk if it does not exist.
if (data == null && loadIfNoCache) { /// 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(); _loadData();
} }
return data; return data;
} }
Future<Either<FieldTypeOptionData, FlowyError>> getTypeOptionData() { /// Return the FieldTypeOptionDataPB that can be parsed into corresponding class using the [parser].
return _fieldService.getFieldTypeOptionData(fieldType: fieldType); /// [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 { void saveCellData(D data, {bool deduplicate = false, void Function(Option<FlowyError>)? resultCallback}) async {
if (deduplicate) { if (deduplicate) {
_loadDataOperation?.cancel(); _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) { if (resultCallback != null) {
resultCallback(result); resultCallback(result);
} }
}); });
} else { } else {
final result = await cellDataPersistence.save(data); final result = await _cellDataPersistence.save(data);
if (resultCallback != null) { if (resultCallback != null) {
resultCallback(result); resultCallback(result);
} }
@ -209,26 +259,59 @@ class _GridCellContext<T, D> extends Equatable {
} }
void _loadData() { void _loadData() {
_saveDataOperation?.cancel();
_loadDataOperation?.cancel(); _loadDataOperation?.cancel();
_loadDataOperation = Timer(const Duration(milliseconds: 10), () { _loadDataOperation = Timer(const Duration(milliseconds: 10), () {
cellDataLoader.loadData().then((data) { _cellDataLoader.loadData().then((data) {
_cellDataNotifier?.value = data; _cellDataNotifier?.value = data;
cellCache.insert(_GridCellCacheObject(key: _cacheKey, object: data)); _cellsCache.insert(_cacheKey, GridCell(object: data));
}); });
}); });
} }
void dispose() { void dispose() {
if (_isDispose) {
Log.error("$this should only dispose once");
return;
}
_isDispose = true;
_cellListener.stop(); _cellListener.stop();
_loadDataOperation?.cancel(); _loadDataOperation?.cancel();
_saveDataOperation?.cancel(); _saveDataOperation?.cancel();
_cellDataNotifier = null;
if (_onFieldChangedFn != null) { if (_onFieldChangedFn != null) {
cellCache.removeFieldListener(_cacheKey, _onFieldChangedFn!); _fieldNotifier.unregister(_cacheKey, _onFieldChangedFn!);
_onFieldChangedFn = null; _onFieldChangedFn = null;
} }
} }
@override @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'; part 'checkbox_cell_bloc.freezed.dart';
class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> { class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
final GridCellContext cellContext; final GridCellController cellContext;
void Function()? _onCellChangedFn; void Function()? _onCellChangedFn;
CheckboxCellBloc({ CheckboxCellBloc({
@ -67,7 +67,7 @@ class CheckboxCellState with _$CheckboxCellState {
required bool isSelected, required bool isSelected,
}) = _CheckboxCellState; }) = _CheckboxCellState;
factory CheckboxCellState.initial(GridCellContext context) { factory CheckboxCellState.initial(GridCellController context) {
return CheckboxCellState(isSelected: _isSelected(context.getCellData())); 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-code/code.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.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.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:table_calendar/table_calendar.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'; part 'date_cal_bloc.freezed.dart';
class DateCalBloc extends Bloc<DateCalEvent, DateCalState> { class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
final GridDateCellContext cellContext; final GridDateCellController cellContext;
void Function()? _onCellChangedFn; void Function()? _onCellChangedFn;
DateCalBloc({ DateCalBloc({
required DateTypeOption dateTypeOption, required DateTypeOption dateTypeOption,
required DateCellData? cellData, required DateCellDataPB? cellData,
required this.cellContext, required this.cellContext,
}) : super(DateCalState.initial(dateTypeOption, cellData)) { }) : super(DateCalState.initial(dateTypeOption, cellData)) {
on<DateCalEvent>( on<DateCalEvent>(
@ -37,7 +38,7 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
setFocusedDay: (focusedDay) { setFocusedDay: (focusedDay) {
emit(state.copyWith(focusedDay: focusedDay)); emit(state.copyWith(focusedDay: focusedDay));
}, },
didReceiveCellUpdate: (DateCellData? cellData) { didReceiveCellUpdate: (DateCellDataPB? cellData) {
final calData = calDataFromCellData(cellData); final calData = calDataFromCellData(cellData);
final time = calData.foldRight("", (dateData, previous) => dateData.time); final time = calData.foldRight("", (dateData, previous) => dateData.time);
emit(state.copyWith(calData: calData, time: 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.setDateFormat(DateFormat dateFormat) = _DateFormat;
const factory DateCalEvent.setIncludeTime(bool includeTime) = _IncludeTime; const factory DateCalEvent.setIncludeTime(bool includeTime) = _IncludeTime;
const factory DateCalEvent.setTime(String time) = _Time; 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) = const factory DateCalEvent.didUpdateCalData(Option<CalendarData> data, Option<String> timeFormatError) =
_DidUpdateCalData; _DidUpdateCalData;
} }
@ -206,7 +207,7 @@ class DateCalState with _$DateCalState {
factory DateCalState.initial( factory DateCalState.initial(
DateTypeOption dateTypeOption, DateTypeOption dateTypeOption,
DateCellData? cellData, DateCellDataPB? cellData,
) { ) {
Option<CalendarData> calData = calDataFromCellData(cellData); Option<CalendarData> calData = calDataFromCellData(cellData);
final time = calData.foldRight("", (dateData, previous) => dateData.time); final time = calData.foldRight("", (dateData, previous) => dateData.time);
@ -232,7 +233,7 @@ String _timeHintText(DateTypeOption typeOption) {
return ""; return "";
} }
Option<CalendarData> calDataFromCellData(DateCellData? cellData) { Option<CalendarData> calDataFromCellData(DateCellDataPB? cellData) {
String? time = timeFromCellData(cellData); String? time = timeFromCellData(cellData);
Option<CalendarData> calData = none(); Option<CalendarData> calData = none();
if (cellData != null) { if (cellData != null) {
@ -248,7 +249,7 @@ $fixnum.Int64 timestampFromDateTime(DateTime dateTime) {
return $fixnum.Int64(timestamp); return $fixnum.Int64(timestamp);
} }
String? timeFromCellData(DateCellData? cellData) { String? timeFromCellData(DateCellDataPB? cellData) {
String? time; String? time;
if (cellData?.hasTime() ?? false) { if (cellData?.hasTime() ?? false) {
time = cellData?.time; 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:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
@ -7,7 +7,7 @@ import 'cell_service/cell_service.dart';
part 'date_cell_bloc.freezed.dart'; part 'date_cell_bloc.freezed.dart';
class DateCellBloc extends Bloc<DateCellEvent, DateCellState> { class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
final GridDateCellContext cellContext; final GridDateCellController cellContext;
void Function()? _onCellChangedFn; void Function()? _onCellChangedFn;
DateCellBloc({required this.cellContext}) : super(DateCellState.initial(cellContext)) { DateCellBloc({required this.cellContext}) : super(DateCellState.initial(cellContext)) {
@ -15,10 +15,10 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
(event, emit) async { (event, emit) async {
event.when( event.when(
initial: () => _startListening(), initial: () => _startListening(),
didReceiveCellUpdate: (DateCellData? cellData) { didReceiveCellUpdate: (DateCellDataPB? cellData) {
emit(state.copyWith(data: cellData, dateStr: _dateStrFromCellData(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 @freezed
class DateCellEvent with _$DateCellEvent { class DateCellEvent with _$DateCellEvent {
const factory DateCellEvent.initial() = _InitialCell; const factory DateCellEvent.initial() = _InitialCell;
const factory DateCellEvent.didReceiveCellUpdate(DateCellData? data) = _DidReceiveCellUpdate; const factory DateCellEvent.didReceiveCellUpdate(DateCellDataPB? data) = _DidReceiveCellUpdate;
const factory DateCellEvent.didReceiveFieldUpdate(Field field) = _DidReceiveFieldUpdate; const factory DateCellEvent.didReceiveFieldUpdate(GridFieldPB field) = _DidReceiveFieldUpdate;
} }
@freezed @freezed
class DateCellState with _$DateCellState { class DateCellState with _$DateCellState {
const factory DateCellState({ const factory DateCellState({
required DateCellData? data, required DateCellDataPB? data,
required String dateStr, required String dateStr,
required Field field, required GridFieldPB field,
}) = _DateCellState; }) = _DateCellState;
factory DateCellState.initial(GridDateCellContext context) { factory DateCellState.initial(GridDateCellController context) {
final cellData = context.getCellData(); final cellData = context.getCellData();
return DateCellState( return DateCellState(
@ -71,7 +71,7 @@ class DateCellState with _$DateCellState {
} }
} }
String _dateStrFromCellData(DateCellData? cellData) { String _dateStrFromCellData(DateCellDataPB? cellData) {
String dateStr = ""; String dateStr = "";
if (cellData != null) { if (cellData != null) {
dateStr = cellData.date + " " + cellData.time; dateStr = cellData.date + " " + cellData.time;

View file

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

View file

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

View file

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

View file

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

View file

@ -6,7 +6,7 @@ import 'cell_service/cell_service.dart';
part 'text_cell_bloc.freezed.dart'; part 'text_cell_bloc.freezed.dart';
class TextCellBloc extends Bloc<TextCellEvent, TextCellState> { class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
final GridCellContext cellContext; final GridCellController cellContext;
void Function()? _onCellChangedFn; void Function()? _onCellChangedFn;
TextCellBloc({ TextCellBloc({
required this.cellContext, required this.cellContext,
@ -63,7 +63,7 @@ class TextCellState with _$TextCellState {
required String content, required String content,
}) = _TextCellState; }) = _TextCellState;
factory TextCellState.initial(GridCellContext context) => TextCellState( factory TextCellState.initial(GridCellController context) => TextCellState(
content: context.getCellData() ?? "", 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:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async'; import 'dart:async';
@ -7,7 +7,7 @@ import 'cell_service/cell_service.dart';
part 'url_cell_bloc.freezed.dart'; part 'url_cell_bloc.freezed.dart';
class URLCellBloc extends Bloc<URLCellEvent, URLCellState> { class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
final GridURLCellContext cellContext; final GridURLCellController cellContext;
void Function()? _onCellChangedFn; void Function()? _onCellChangedFn;
URLCellBloc({ URLCellBloc({
required this.cellContext, required this.cellContext,
@ -57,7 +57,7 @@ class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
class URLCellEvent with _$URLCellEvent { class URLCellEvent with _$URLCellEvent {
const factory URLCellEvent.initial() = _InitialCell; const factory URLCellEvent.initial() = _InitialCell;
const factory URLCellEvent.updateURL(String url) = _UpdateURL; const factory URLCellEvent.updateURL(String url) = _UpdateURL;
const factory URLCellEvent.didReceiveCellUpdate(URLCellData? cell) = _DidReceiveCellUpdate; const factory URLCellEvent.didReceiveCellUpdate(URLCellDataPB? cell) = _DidReceiveCellUpdate;
} }
@freezed @freezed
@ -67,7 +67,7 @@ class URLCellState with _$URLCellState {
required String url, required String url,
}) = _URLCellState; }) = _URLCellState;
factory URLCellState.initial(GridURLCellContext context) { factory URLCellState.initial(GridURLCellController context) {
final cellData = context.getCellData(); final cellData = context.getCellData();
return URLCellState( return URLCellState(
content: cellData?.content ?? "", 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:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async'; import 'dart:async';
@ -7,7 +7,7 @@ import 'cell_service/cell_service.dart';
part 'url_cell_editor_bloc.freezed.dart'; part 'url_cell_editor_bloc.freezed.dart';
class URLCellEditorBloc extends Bloc<URLCellEditorEvent, URLCellEditorState> { class URLCellEditorBloc extends Bloc<URLCellEditorEvent, URLCellEditorState> {
final GridURLCellContext cellContext; final GridURLCellController cellContext;
void Function()? _onCellChangedFn; void Function()? _onCellChangedFn;
URLCellEditorBloc({ URLCellEditorBloc({
required this.cellContext, required this.cellContext,
@ -54,7 +54,7 @@ class URLCellEditorBloc extends Bloc<URLCellEditorEvent, URLCellEditorState> {
@freezed @freezed
class URLCellEditorEvent with _$URLCellEditorEvent { class URLCellEditorEvent with _$URLCellEditorEvent {
const factory URLCellEditorEvent.initial() = _InitialCell; 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; const factory URLCellEditorEvent.updateText(String text) = _UpdateText;
} }
@ -64,7 +64,7 @@ class URLCellEditorState with _$URLCellEditorState {
required String content, required String content,
}) = _URLCellEditorState; }) = _URLCellEditorState;
factory URLCellEditorState.initial(GridURLCellContext context) { factory URLCellEditorState.initial(GridURLCellController context) {
final cellData = context.getCellData(); final cellData = context.getCellData();
return URLCellEditorState( return URLCellEditorState(
content: cellData?.content ?? "", content: cellData?.content ?? "",

View file

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

View file

@ -62,7 +62,7 @@ class FieldCellBloc extends Bloc<FieldCellEvent, FieldCellState> {
@freezed @freezed
class FieldCellEvent with _$FieldCellEvent { class FieldCellEvent with _$FieldCellEvent {
const factory FieldCellEvent.initial() = _InitialCell; 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.startUpdateWidth(double offset) = _StartUpdateWidth;
const factory FieldCellEvent.endUpdateWidth() = _EndUpdateWidth; const factory FieldCellEvent.endUpdateWidth() = _EndUpdateWidth;
} }
@ -71,7 +71,7 @@ class FieldCellEvent with _$FieldCellEvent {
class FieldCellState with _$FieldCellState { class FieldCellState with _$FieldCellState {
const factory FieldCellState({ const factory FieldCellState({
required String gridId, required String gridId,
required Field field, required GridFieldPB field,
required double width, required double width,
}) = _FieldCellState; }) = _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:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async'; import 'dart:async';
@ -6,27 +7,32 @@ import 'package:dartz/dartz.dart';
part 'field_editor_bloc.freezed.dart'; part 'field_editor_bloc.freezed.dart';
class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> { class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
final TypeOptionDataController dataController;
FieldEditorBloc({ FieldEditorBloc({
required String gridId, required String gridId,
required String fieldName, required String fieldName,
required IFieldContextLoader fieldContextLoader, required IFieldTypeOptionLoader loader,
}) : super(FieldEditorState.initial(gridId, fieldName, fieldContextLoader)) { }) : dataController = TypeOptionDataController(gridId: gridId, loader: loader),
super(FieldEditorState.initial(gridId, fieldName)) {
on<FieldEditorEvent>( on<FieldEditorEvent>(
(event, emit) async { (event, emit) async {
await event.when( await event.when(
initial: () async { initial: () async {
final fieldContext = GridFieldContext(gridId: gridId, loader: fieldContextLoader); dataController.addFieldListener((field) {
await fieldContext.loadData().then((result) { if (!isClosed) {
result.fold( add(FieldEditorEvent.didReceiveFieldChanged(field));
(l) => emit(state.copyWith(fieldContext: Some(fieldContext), name: fieldContext.field.name)), }
(r) => null,
);
}); });
await dataController.loadData();
}, },
updateName: (name) { updateName: (name) {
state.fieldContext.fold(() => null, (fieldContext) => fieldContext.fieldName = name); dataController.fieldName = name;
emit(state.copyWith(name: 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 { class FieldEditorEvent with _$FieldEditorEvent {
const factory FieldEditorEvent.initial() = _InitialField; const factory FieldEditorEvent.initial() = _InitialField;
const factory FieldEditorEvent.updateName(String name) = _UpdateName; const factory FieldEditorEvent.updateName(String name) = _UpdateName;
const factory FieldEditorEvent.didReceiveFieldChanged(GridFieldPB field) = _DidReceiveFieldChanged;
} }
@freezed @freezed
@ -50,13 +57,17 @@ class FieldEditorState with _$FieldEditorState {
required String gridId, required String gridId,
required String errorText, required String errorText,
required String name, required String name,
required Option<GridFieldContext> fieldContext, required Option<GridFieldPB> field,
}) = _FieldEditorState; }) = _FieldEditorState;
factory FieldEditorState.initial(String gridId, String fieldName, IFieldContextLoader loader) => FieldEditorState( factory FieldEditorState.initial(
String gridId,
String fieldName,
) =>
FieldEditorState(
gridId: gridId, gridId: gridId,
fieldContext: none(),
errorText: '', errorText: '',
field: none(),
name: fieldName, 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 'dart:typed_data';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
typedef UpdateFieldNotifiedValue = Either<Field, FlowyError>; typedef UpdateFieldNotifiedValue = Either<GridFieldPB, FlowyError>;
class SingleFieldListener { class SingleFieldListener {
final String fieldId; final String fieldId;
@ -31,7 +31,7 @@ class SingleFieldListener {
switch (ty) { switch (ty) {
case GridNotification.DidUpdateField: case GridNotification.DidUpdateField:
result.fold( result.fold(
(payload) => _updateFieldNotifier?.value = left(Field.fromBuffer(payload)), (payload) => _updateFieldNotifier?.value = left(GridFieldPB.fromBuffer(payload)),
(error) => _updateFieldNotifier?.value = right(error), (error) => _updateFieldNotifier?.value = right(error),
); );
break; break;

View file

@ -1,4 +1,5 @@
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:flowy_infra/notifier.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart'; import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.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'; import 'package:protobuf/protobuf.dart';
part 'field_service.freezed.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 { class FieldService {
final String gridId; final String gridId;
final String fieldId; final String fieldId;
@ -16,10 +21,10 @@ class FieldService {
FieldService({required this.gridId, required this.fieldId}); FieldService({required this.gridId, required this.fieldId});
Future<Either<Unit, FlowyError>> moveField(int fromIndex, int toIndex) { Future<Either<Unit, FlowyError>> moveField(int fromIndex, int toIndex) {
final payload = MoveItemPayload.create() final payload = MoveItemPayloadPB.create()
..gridId = gridId ..gridId = gridId
..itemId = fieldId ..itemId = fieldId
..ty = MoveItemType.MoveField ..ty = MoveItemTypePB.MoveField
..fromIndex = fromIndex ..fromIndex = fromIndex
..toIndex = toIndex; ..toIndex = toIndex;
@ -34,7 +39,7 @@ class FieldService {
double? width, double? width,
List<int>? typeOptionData, List<int>? typeOptionData,
}) { }) {
var payload = FieldChangesetPayload.create() var payload = FieldChangesetPayloadPB.create()
..gridId = gridId ..gridId = gridId
..fieldId = fieldId; ..fieldId = fieldId;
@ -68,11 +73,11 @@ class FieldService {
// Create the field if it does not exist. Otherwise, update the field. // Create the field if it does not exist. Otherwise, update the field.
static Future<Either<Unit, FlowyError>> insertField({ static Future<Either<Unit, FlowyError>> insertField({
required String gridId, required String gridId,
required Field field, required GridFieldPB field,
List<int>? typeOptionData, List<int>? typeOptionData,
String? startFieldId, String? startFieldId,
}) { }) {
var payload = InsertFieldPayload.create() var payload = InsertFieldPayloadPB.create()
..gridId = gridId ..gridId = gridId
..field_2 = field ..field_2 = field
..typeOptionData = typeOptionData ?? []; ..typeOptionData = typeOptionData ?? [];
@ -89,7 +94,7 @@ class FieldService {
required String fieldId, required String fieldId,
required List<int> typeOptionData, required List<int> typeOptionData,
}) { }) {
var payload = UpdateFieldTypeOptionPayload.create() var payload = UpdateFieldTypeOptionPayloadPB.create()
..gridId = gridId ..gridId = gridId
..fieldId = fieldId ..fieldId = fieldId
..typeOptionData = typeOptionData; ..typeOptionData = typeOptionData;
@ -98,7 +103,7 @@ class FieldService {
} }
Future<Either<Unit, FlowyError>> deleteField() { Future<Either<Unit, FlowyError>> deleteField() {
final payload = FieldIdentifierPayload.create() final payload = GridFieldIdentifierPayloadPB.create()
..gridId = gridId ..gridId = gridId
..fieldId = fieldId; ..fieldId = fieldId;
@ -106,17 +111,17 @@ class FieldService {
} }
Future<Either<Unit, FlowyError>> duplicateField() { Future<Either<Unit, FlowyError>> duplicateField() {
final payload = FieldIdentifierPayload.create() final payload = GridFieldIdentifierPayloadPB.create()
..gridId = gridId ..gridId = gridId
..fieldId = fieldId; ..fieldId = fieldId;
return GridEventDuplicateField(payload).send(); return GridEventDuplicateField(payload).send();
} }
Future<Either<FieldTypeOptionData, FlowyError>> getFieldTypeOptionData({ Future<Either<FieldTypeOptionDataPB, FlowyError>> getFieldTypeOptionData({
required FieldType fieldType, required FieldType fieldType,
}) { }) {
final payload = EditFieldPayload.create() final payload = EditFieldPayloadPB.create()
..gridId = gridId ..gridId = gridId
..fieldId = fieldId ..fieldId = fieldId
..fieldType = fieldType; ..fieldType = fieldType;
@ -133,16 +138,16 @@ class FieldService {
class GridFieldCellContext with _$GridFieldCellContext { class GridFieldCellContext with _$GridFieldCellContext {
const factory GridFieldCellContext({ const factory GridFieldCellContext({
required String gridId, required String gridId,
required Field field, required GridFieldPB field,
}) = _GridFieldCellContext; }) = _GridFieldCellContext;
} }
abstract class IFieldContextLoader { abstract class IFieldTypeOptionLoader {
String get gridId; String get gridId;
Future<Either<FieldTypeOptionData, FlowyError>> load(); Future<Either<FieldTypeOptionDataPB, FlowyError>> load();
Future<Either<FieldTypeOptionData, FlowyError>> switchToField(String fieldId, FieldType fieldType) { Future<Either<FieldTypeOptionDataPB, FlowyError>> switchToField(String fieldId, FieldType fieldType) {
final payload = EditFieldPayload.create() final payload = EditFieldPayloadPB.create()
..gridId = gridId ..gridId = gridId
..fieldId = fieldId ..fieldId = fieldId
..fieldType = fieldType; ..fieldType = fieldType;
@ -151,16 +156,16 @@ abstract class IFieldContextLoader {
} }
} }
class NewFieldContextLoader extends IFieldContextLoader { class NewFieldTypeOptionLoader extends IFieldTypeOptionLoader {
@override @override
final String gridId; final String gridId;
NewFieldContextLoader({ NewFieldTypeOptionLoader({
required this.gridId, required this.gridId,
}); });
@override @override
Future<Either<FieldTypeOptionData, FlowyError>> load() { Future<Either<FieldTypeOptionDataPB, FlowyError>> load() {
final payload = EditFieldPayload.create() final payload = EditFieldPayloadPB.create()
..gridId = gridId ..gridId = gridId
..fieldType = FieldType.RichText; ..fieldType = FieldType.RichText;
@ -168,19 +173,19 @@ class NewFieldContextLoader extends IFieldContextLoader {
} }
} }
class FieldContextLoader extends IFieldContextLoader { class FieldTypeOptionLoader extends IFieldTypeOptionLoader {
@override @override
final String gridId; final String gridId;
final Field field; final GridFieldPB field;
FieldContextLoader({ FieldTypeOptionLoader({
required this.gridId, required this.gridId,
required this.field, required this.field,
}); });
@override @override
Future<Either<FieldTypeOptionData, FlowyError>> load() { Future<Either<FieldTypeOptionDataPB, FlowyError>> load() {
final payload = EditFieldPayload.create() final payload = EditFieldPayloadPB.create()
..gridId = gridId ..gridId = gridId
..fieldId = field.id ..fieldId = field.id
..fieldType = field.fieldType; ..fieldType = field.fieldType;
@ -189,16 +194,16 @@ class FieldContextLoader extends IFieldContextLoader {
} }
} }
class GridFieldContext { class TypeOptionDataController {
final String gridId; final String gridId;
final IFieldContextLoader _loader; final IFieldTypeOptionLoader _loader;
late FieldTypeOptionData _data; late FieldTypeOptionDataPB _data;
ValueNotifier<Field>? _fieldNotifier; final PublishNotifier<GridFieldPB> _fieldNotifier = PublishNotifier();
GridFieldContext({ TypeOptionDataController({
required this.gridId, required this.gridId,
required IFieldContextLoader loader, required IFieldTypeOptionLoader loader,
}) : _loader = loader; }) : _loader = loader;
Future<Either<Unit, FlowyError>> loadData() async { Future<Either<Unit, FlowyError>> loadData() async {
@ -207,13 +212,7 @@ class GridFieldContext {
(data) { (data) {
data.freeze(); data.freeze();
_data = data; _data = data;
_fieldNotifier.value = data.field_2;
if (_fieldNotifier == null) {
_fieldNotifier = ValueNotifier(data.field_2);
} else {
_fieldNotifier?.value = data.field_2;
}
return left(unit); return left(unit);
}, },
(err) { (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); _updateData(newField: field);
} }
@ -239,7 +238,7 @@ class GridFieldContext {
_updateData(newTypeOptionData: typeOptionData); _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) { _data = _data.rebuild((rebuildData) {
if (newName != null) { if (newName != null) {
rebuildData.field_2 = rebuildData.field_2.rebuild((rebuildField) { 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( FieldService.insertField(
gridId: gridId, gridId: gridId,
@ -283,16 +280,16 @@ class GridFieldContext {
}); });
} }
void Function() addFieldListener(void Function(Field) callback) { void Function() addFieldListener(void Function(GridFieldPB) callback) {
listener() { listener() {
callback(field); callback(field);
} }
_fieldNotifier?.addListener(listener); _fieldNotifier.addListener(listener);
return listener; return listener;
} }
void removeFieldListener(void Function() 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 'dart:typed_data';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
typedef UpdateFieldNotifiedValue = Either<GridFieldChangeset, FlowyError>; typedef UpdateFieldNotifiedValue = Either<GridFieldChangesetPB, FlowyError>;
class GridFieldsListener { class GridFieldsListener {
final String gridId; final String gridId;
@ -27,7 +27,7 @@ class GridFieldsListener {
switch (ty) { switch (ty) {
case GridNotification.DidUpdateGridField: case GridNotification.DidUpdateGridField:
result.fold( result.fold(
(payload) => updateFieldsNotifier?.value = left(GridFieldChangeset.fromBuffer(payload)), (payload) => updateFieldsNotifier?.value = left(GridFieldChangesetPB.fromBuffer(payload)),
(error) => updateFieldsNotifier?.value = right(error), (error) => updateFieldsNotifier?.value = right(error),
); );
break; break;

View file

@ -1,14 +1,15 @@
import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart'; 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.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async'; import 'dart:async';
import 'package:protobuf/protobuf.dart'; import 'package:protobuf/protobuf.dart';
part 'date_bloc.freezed.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 @override
DateTypeOption fromBuffer(List<int> buffer) { DateTypeOption fromBuffer(List<int> buffer) {
return DateTypeOption.fromBuffer(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:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async'; import 'dart:async';
@ -7,7 +7,7 @@ import 'package:dartz/dartz.dart';
part 'edit_select_option_bloc.freezed.dart'; part 'edit_select_option_bloc.freezed.dart';
class EditSelectOptionBloc extends Bloc<EditSelectOptionEvent, EditSelectOptionState> { class EditSelectOptionBloc extends Bloc<EditSelectOptionEvent, EditSelectOptionState> {
EditSelectOptionBloc({required SelectOption option}) : super(EditSelectOptionState.initial(option)) { EditSelectOptionBloc({required SelectOptionPB option}) : super(EditSelectOptionState.initial(option)) {
on<EditSelectOptionEvent>( on<EditSelectOptionEvent>(
(event, emit) async { (event, emit) async {
event.map( event.map(
@ -30,14 +30,14 @@ class EditSelectOptionBloc extends Bloc<EditSelectOptionEvent, EditSelectOptionS
return super.close(); return super.close();
} }
SelectOption _updateColor(SelectOptionColor color) { SelectOptionPB _updateColor(SelectOptionColorPB color) {
state.option.freeze(); state.option.freeze();
return state.option.rebuild((option) { return state.option.rebuild((option) {
option.color = color; option.color = color;
}); });
} }
SelectOption _updateName(String name) { SelectOptionPB _updateName(String name) {
state.option.freeze(); state.option.freeze();
return state.option.rebuild((option) { return state.option.rebuild((option) {
option.name = name; option.name = name;
@ -48,18 +48,18 @@ class EditSelectOptionBloc extends Bloc<EditSelectOptionEvent, EditSelectOptionS
@freezed @freezed
class EditSelectOptionEvent with _$EditSelectOptionEvent { class EditSelectOptionEvent with _$EditSelectOptionEvent {
const factory EditSelectOptionEvent.updateName(String name) = _UpdateName; 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; const factory EditSelectOptionEvent.delete() = _Delete;
} }
@freezed @freezed
class EditSelectOptionState with _$EditSelectOptionState { class EditSelectOptionState with _$EditSelectOptionState {
const factory EditSelectOptionState({ const factory EditSelectOptionState({
required SelectOption option, required SelectOptionPB option,
required Option<bool> deleted, required Option<bool> deleted,
}) = _EditSelectOptionState; }) = _EditSelectOptionState;
factory EditSelectOptionState.initial(SelectOption option) => EditSelectOptionState( factory EditSelectOptionState.initial(SelectOptionPB option) => EditSelectOptionState(
option: option, option: option,
deleted: none(), deleted: none(),
); );

View file

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

View file

@ -8,9 +8,9 @@ import 'package:protobuf/protobuf.dart';
part 'number_bloc.freezed.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 @override
NumberTypeOption fromBuffer(List<int> buffer) { NumberTypeOption fromBuffer(List<int> buffer) {
return NumberTypeOption.fromBuffer(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:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async'; import 'dart:async';
@ -6,25 +6,25 @@ import 'package:dartz/dartz.dart';
part 'select_option_type_option_bloc.freezed.dart'; part 'select_option_type_option_bloc.freezed.dart';
abstract class SelectOptionTypeOptionAction { 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> { class SelectOptionTypeOptionBloc extends Bloc<SelectOptionTypeOptionEvent, SelectOptionTypeOptionState> {
final SelectOptionTypeOptionAction typeOptionAction; final SelectOptionTypeOptionAction typeOptionAction;
SelectOptionTypeOptionBloc({ SelectOptionTypeOptionBloc({
required List<SelectOption> options, required List<SelectOptionPB> options,
required this.typeOptionAction, required this.typeOptionAction,
}) : super(SelectOptionTypeOptionState.initial(options)) { }) : super(SelectOptionTypeOptionState.initial(options)) {
on<SelectOptionTypeOptionEvent>( on<SelectOptionTypeOptionEvent>(
(event, emit) async { (event, emit) async {
await event.when( await event.when(
createOption: (optionName) async { createOption: (optionName) async {
final List<SelectOption> options = await typeOptionAction.insertOption(optionName); final List<SelectOptionPB> options = await typeOptionAction.insertOption(optionName);
emit(state.copyWith(options: options)); emit(state.copyWith(options: options));
}, },
addingOption: () { addingOption: () {
@ -34,11 +34,11 @@ class SelectOptionTypeOptionBloc extends Bloc<SelectOptionTypeOptionEvent, Selec
emit(state.copyWith(isEditingOption: false, newOptionName: none())); emit(state.copyWith(isEditingOption: false, newOptionName: none()));
}, },
updateOption: (option) { updateOption: (option) {
final List<SelectOption> options = typeOptionAction.udpateOption(option); final List<SelectOptionPB> options = typeOptionAction.udpateOption(option);
emit(state.copyWith(options: options)); emit(state.copyWith(options: options));
}, },
deleteOption: (option) { deleteOption: (option) {
final List<SelectOption> options = typeOptionAction.deleteOption(option); final List<SelectOptionPB> options = typeOptionAction.deleteOption(option);
emit(state.copyWith(options: options)); emit(state.copyWith(options: options));
}, },
); );
@ -57,19 +57,19 @@ class SelectOptionTypeOptionEvent with _$SelectOptionTypeOptionEvent {
const factory SelectOptionTypeOptionEvent.createOption(String optionName) = _CreateOption; const factory SelectOptionTypeOptionEvent.createOption(String optionName) = _CreateOption;
const factory SelectOptionTypeOptionEvent.addingOption() = _AddingOption; const factory SelectOptionTypeOptionEvent.addingOption() = _AddingOption;
const factory SelectOptionTypeOptionEvent.endAddingOption() = _EndAddingOption; const factory SelectOptionTypeOptionEvent.endAddingOption() = _EndAddingOption;
const factory SelectOptionTypeOptionEvent.updateOption(SelectOption option) = _UpdateOption; const factory SelectOptionTypeOptionEvent.updateOption(SelectOptionPB option) = _UpdateOption;
const factory SelectOptionTypeOptionEvent.deleteOption(SelectOption option) = _DeleteOption; const factory SelectOptionTypeOptionEvent.deleteOption(SelectOptionPB option) = _DeleteOption;
} }
@freezed @freezed
class SelectOptionTypeOptionState with _$SelectOptionTypeOptionState { class SelectOptionTypeOptionState with _$SelectOptionTypeOptionState {
const factory SelectOptionTypeOptionState({ const factory SelectOptionTypeOptionState({
required List<SelectOption> options, required List<SelectOptionPB> options,
required bool isEditingOption, required bool isEditingOption,
required Option<String> newOptionName, required Option<String> newOptionName,
}) = _SelectOptionTyepOptionState; }) = _SelectOptionTyepOptionState;
factory SelectOptionTypeOptionState.initial(List<SelectOption> options) => SelectOptionTypeOptionState( factory SelectOptionTypeOptionState.initial(List<SelectOptionPB> options) => SelectOptionTypeOptionState(
options: options, options: options,
isEditingOption: false, isEditingOption: false,
newOptionName: none(), newOptionName: none(),

View file

@ -1,27 +1,28 @@
import 'package:app_flowy/workspace/application/grid/field/field_service.dart'; import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
import 'package:flowy_sdk/log.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 'dart:async';
import 'package:protobuf/protobuf.dart'; import 'package:protobuf/protobuf.dart';
import 'select_option_type_option_bloc.dart'; import 'select_option_type_option_bloc.dart';
import 'type_option_service.dart'; import 'type_option_service.dart';
class SingleSelectTypeOptionContext extends TypeOptionContext<SingleSelectTypeOption> class SingleSelectTypeOptionContext extends TypeOptionWidgetContext<SingleSelectTypeOptionPB>
with SelectOptionTypeOptionAction { with SelectOptionTypeOptionAction {
final TypeOptionService service; final TypeOptionService service;
SingleSelectTypeOptionContext({ SingleSelectTypeOptionContext({
required SingleSelectTypeOptionDataBuilder dataBuilder, required SingleSelectTypeOptionWidgetDataParser dataBuilder,
required GridFieldContext fieldContext, required TypeOptionDataController fieldContext,
}) : service = TypeOptionService( }) : service = TypeOptionService(
gridId: fieldContext.gridId, gridId: fieldContext.gridId,
fieldId: fieldContext.field.id, fieldId: fieldContext.field.id,
), ),
super(dataBuilder: dataBuilder, fieldContext: fieldContext); super(dataParser: dataBuilder, dataController: fieldContext);
@override @override
List<SelectOption> Function(SelectOption) get deleteOption { List<SelectOptionPB> Function(SelectOptionPB) get deleteOption {
return (SelectOption option) { return (SelectOptionPB option) {
typeOption.freeze(); typeOption.freeze();
typeOption = typeOption.rebuild((typeOption) { typeOption = typeOption.rebuild((typeOption) {
final index = typeOption.options.indexWhere((element) => element.id == option.id); final index = typeOption.options.indexWhere((element) => element.id == option.id);
@ -34,7 +35,7 @@ class SingleSelectTypeOptionContext extends TypeOptionContext<SingleSelectTypeOp
} }
@override @override
Future<List<SelectOption>> Function(String) get insertOption { Future<List<SelectOptionPB>> Function(String) get insertOption {
return (String optionName) { return (String optionName) {
return service.newOption(name: optionName).then((result) { return service.newOption(name: optionName).then((result) {
return result.fold( return result.fold(
@ -56,8 +57,8 @@ class SingleSelectTypeOptionContext extends TypeOptionContext<SingleSelectTypeOp
} }
@override @override
List<SelectOption> Function(SelectOption) get udpateOption { List<SelectOptionPB> Function(SelectOptionPB) get udpateOption {
return (SelectOption option) { return (SelectOptionPB option) {
typeOption.freeze(); typeOption.freeze();
typeOption = typeOption.rebuild((typeOption) { typeOption = typeOption.rebuild((typeOption) {
final index = typeOption.options.indexWhere((element) => element.id == option.id); 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 @override
SingleSelectTypeOption fromBuffer(List<int> buffer) { SingleSelectTypeOptionPB fromBuffer(List<int> buffer) {
return SingleSelectTypeOption.fromBuffer(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-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.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/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'; import 'package:protobuf/protobuf.dart';
class TypeOptionService { class TypeOptionService {
@ -18,14 +18,14 @@ class TypeOptionService {
required this.fieldId, required this.fieldId,
}); });
Future<Either<SelectOption, FlowyError>> newOption({ Future<Either<SelectOptionPB, FlowyError>> newOption({
required String name, required String name,
}) { }) {
final fieldIdentifier = FieldIdentifierPayload.create() final fieldIdentifier = GridFieldIdentifierPayloadPB.create()
..gridId = gridId ..gridId = gridId
..fieldId = fieldId; ..fieldId = fieldId;
final payload = CreateSelectOptionPayload.create() final payload = CreateSelectOptionPayloadPB.create()
..optionName = name ..optionName = name
..fieldIdentifier = fieldIdentifier; ..fieldIdentifier = fieldIdentifier;
@ -33,36 +33,36 @@ class TypeOptionService {
} }
} }
abstract class TypeOptionDataBuilder<T> { abstract class TypeOptionDataParser<T> {
T fromBuffer(List<int> buffer); T fromBuffer(List<int> buffer);
} }
class TypeOptionContext<T extends GeneratedMessage> { class TypeOptionWidgetContext<T extends GeneratedMessage> {
T? _typeOptionObject; T? _typeOptionObject;
final GridFieldContext _fieldContext; final TypeOptionDataController _dataController;
final TypeOptionDataBuilder<T> dataBuilder; final TypeOptionDataParser<T> dataParser;
TypeOptionContext({ TypeOptionWidgetContext({
required this.dataBuilder, required this.dataParser,
required GridFieldContext fieldContext, required TypeOptionDataController dataController,
}) : _fieldContext = fieldContext; }) : _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 { T get typeOption {
if (_typeOptionObject != null) { if (_typeOptionObject != null) {
return _typeOptionObject!; return _typeOptionObject!;
} }
final T object = dataBuilder.fromBuffer(_fieldContext.typeOptionData); final T object = dataParser.fromBuffer(_dataController.typeOptionData);
_typeOptionObject = object; _typeOptionObject = object;
return object; return object;
} }
set typeOption(T typeOption) { set typeOption(T typeOption) {
_fieldContext.typeOptionData = typeOption.writeToBuffer(); _dataController.typeOptionData = typeOption.writeToBuffer();
_typeOptionObject = typeOption; _typeOptionObject = typeOption;
} }
} }
@ -74,10 +74,10 @@ abstract class TypeOptionFieldDelegate {
class TypeOptionContext2<T> { class TypeOptionContext2<T> {
final String gridId; final String gridId;
final Field field; final GridFieldPB field;
final FieldService _fieldService; final FieldService _fieldService;
T? _data; T? _data;
final TypeOptionDataBuilder dataBuilder; final TypeOptionDataParser dataBuilder;
TypeOptionContext2({ TypeOptionContext2({
required this.gridId, 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:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'block/block_service.dart'; import 'block/block_cache.dart';
import 'grid_service.dart'; import 'grid_service.dart';
import 'row/row_service.dart'; import 'row/row_service.dart';
import 'dart:collection'; import 'dart:collection';
@ -20,17 +20,17 @@ class GridBloc extends Bloc<GridEvent, GridState> {
final GridFieldCache fieldCache; final GridFieldCache fieldCache;
// key: the block id // key: the block id
final LinkedHashMap<String, GridBlockCacheService> _blocks; final LinkedHashMap<String, GridBlockCache> _blocks;
List<GridRow> get rows { List<GridRowInfo> get rowInfos {
final List<GridRow> rows = []; final List<GridRowInfo> rows = [];
for (var block in _blocks.values) { for (var block in _blocks.values) {
rows.addAll(block.rows); rows.addAll(block.rows);
} }
return rows; return rows;
} }
GridBloc({required View view}) GridBloc({required ViewPB view})
: gridId = view.id, : gridId = view.id,
_blocks = LinkedHashMap.identity(), _blocks = LinkedHashMap.identity(),
_gridService = GridService(gridId: view.id), _gridService = GridService(gridId: view.id),
@ -46,11 +46,11 @@ class GridBloc extends Bloc<GridEvent, GridState> {
createRow: () { createRow: () {
_gridService.createRow(); _gridService.createRow();
}, },
didReceiveRowUpdate: (rows, reason) { didReceiveRowUpdate: (newRowInfos, reason) {
emit(state.copyWith(rows: rows, reason: reason)); emit(state.copyWith(rowInfos: newRowInfos, reason: reason));
}, },
didReceiveFieldUpdate: (fields) { 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(); return super.close();
} }
GridRowCacheService? getRowCache(String blockId, String rowId) { GridRowCache? getRowCache(String blockId, String rowId) {
final GridBlockCacheService? blockCache = _blocks[blockId]; final GridBlockCache? blockCache = _blocks[blockId];
return blockCache?.rowCache; return blockCache?.rowCache;
} }
@ -93,8 +93,8 @@ class GridBloc extends Bloc<GridEvent, GridState> {
); );
} }
Future<void> _loadFields(Grid grid, Emitter<GridState> emit) async { Future<void> _loadFields(GridPB grid, Emitter<GridState> emit) async {
final result = await _gridService.getFields(fieldOrders: grid.fieldOrders); final result = await _gridService.getFields(fieldIds: grid.fields);
return Future( return Future(
() => result.fold( () => result.fold(
(fields) { (fields) {
@ -103,7 +103,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
emit(state.copyWith( emit(state.copyWith(
grid: Some(grid), grid: Some(grid),
fields: GridFieldEquatable(fieldCache.fields), fields: GridFieldEquatable(fieldCache.fields),
rows: rows, rowInfos: rowInfos,
loadingState: GridLoadingState.finish(left(unit)), 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) { for (final block in blocks) {
if (_blocks[block.id] != null) { if (_blocks[block.id] != null) {
Log.warn("Intial duplicate block's cache: ${block.id}"); Log.warn("Intial duplicate block's cache: ${block.id}");
return; return;
} }
final cache = GridBlockCacheService( final cache = GridBlockCache(
gridId: gridId, gridId: gridId,
block: block, block: block,
fieldCache: fieldCache, fieldCache: fieldCache,
@ -127,7 +127,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
cache.addListener( cache.addListener(
listenWhen: () => !isClosed, listenWhen: () => !isClosed,
onChangeReason: (reason) => add(GridEvent.didReceiveRowUpdate(rows, reason)), onChangeReason: (reason) => add(GridEvent.didReceiveRowUpdate(rowInfos, reason)),
); );
_blocks[block.id] = cache; _blocks[block.id] = cache;
@ -139,24 +139,25 @@ class GridBloc extends Bloc<GridEvent, GridState> {
class GridEvent with _$GridEvent { class GridEvent with _$GridEvent {
const factory GridEvent.initial() = InitialGrid; const factory GridEvent.initial() = InitialGrid;
const factory GridEvent.createRow() = _CreateRow; const factory GridEvent.createRow() = _CreateRow;
const factory GridEvent.didReceiveRowUpdate(List<GridRow> rows, GridRowChangeReason listState) = _DidReceiveRowUpdate; const factory GridEvent.didReceiveRowUpdate(List<GridRowInfo> rows, GridRowChangeReason listState) =
const factory GridEvent.didReceiveFieldUpdate(List<Field> fields) = _DidReceiveFieldUpdate; _DidReceiveRowUpdate;
const factory GridEvent.didReceiveFieldUpdate(List<GridFieldPB> fields) = _DidReceiveFieldUpdate;
} }
@freezed @freezed
class GridState with _$GridState { class GridState with _$GridState {
const factory GridState({ const factory GridState({
required String gridId, required String gridId,
required Option<Grid> grid, required Option<GridPB> grid,
required GridFieldEquatable fields, required GridFieldEquatable fields,
required List<GridRow> rows, required List<GridRowInfo> rowInfos,
required GridLoadingState loadingState, required GridLoadingState loadingState,
required GridRowChangeReason reason, required GridRowChangeReason reason,
}) = _GridState; }) = _GridState;
factory GridState.initial(String gridId) => GridState( factory GridState.initial(String gridId) => GridState(
fields: const GridFieldEquatable([]), fields: const GridFieldEquatable([]),
rows: [], rowInfos: [],
grid: none(), grid: none(),
gridId: gridId, gridId: gridId,
loadingState: const _Loading(), loadingState: const _Loading(),
@ -171,8 +172,8 @@ class GridLoadingState with _$GridLoadingState {
} }
class GridFieldEquatable extends Equatable { class GridFieldEquatable extends Equatable {
final List<Field> _fields; final List<GridFieldPB> _fields;
const GridFieldEquatable(List<Field> fields) : _fields = fields; const GridFieldEquatable(List<GridFieldPB> fields) : _fields = fields;
@override @override
List<Object?> get props { 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 { 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)); fields.insert(value.toIndex, fields.removeAt(value.fromIndex));
emit(state.copyWith(fields: fields)); emit(state.copyWith(fields: fields));
@ -62,16 +62,16 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
@freezed @freezed
class GridHeaderEvent with _$GridHeaderEvent { class GridHeaderEvent with _$GridHeaderEvent {
const factory GridHeaderEvent.initial() = _InitialHeader; const factory GridHeaderEvent.initial() = _InitialHeader;
const factory GridHeaderEvent.didReceiveFieldUpdate(List<Field> fields) = _DidReceiveFieldUpdate; const factory GridHeaderEvent.didReceiveFieldUpdate(List<GridFieldPB> fields) = _DidReceiveFieldUpdate;
const factory GridHeaderEvent.moveField(Field field, int fromIndex, int toIndex) = _MoveField; const factory GridHeaderEvent.moveField(GridFieldPB field, int fromIndex, int toIndex) = _MoveField;
} }
@freezed @freezed
class GridHeaderState with _$GridHeaderState { 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) { factory GridHeaderState.initial(List<GridFieldPB> fields) {
// final List<Field> newFields = List.from(fields); // final List<GridFieldPB> newFields = List.from(fields);
// newFields.retainWhere((field) => field.visibility); // newFields.retainWhere((field) => field.visibility);
return GridHeaderState(fields: fields); return GridHeaderState(fields: fields);
} }

View file

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

View file

@ -4,18 +4,18 @@ export 'row/row_service.dart';
export 'grid_service.dart'; export 'grid_service.dart';
export 'grid_header_bloc.dart'; export 'grid_header_bloc.dart';
// Field // GridFieldPB
export 'field/field_service.dart'; export 'field/field_service.dart';
export 'field/field_action_sheet_bloc.dart'; export 'field/field_action_sheet_bloc.dart';
export 'field/field_editor_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/date_bloc.dart';
export 'field/type_option/number_bloc.dart'; export 'field/type_option/number_bloc.dart';
export 'field/type_option/single_select_type_option.dart'; export 'field/type_option/single_select_type_option.dart';
// Cell // GridCellPB
export 'cell/text_cell_bloc.dart'; export 'cell/text_cell_bloc.dart';
export 'cell/number_cell_bloc.dart'; export 'cell/number_cell_bloc.dart';
export 'cell/select_option_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> { class RowActionSheetBloc extends Bloc<RowActionSheetEvent, RowActionSheetState> {
final RowService _rowService; final RowService _rowService;
RowActionSheetBloc({required GridRow rowData}) RowActionSheetBloc({required GridRowInfo rowData})
: _rowService = RowService( : _rowService = RowService(
gridId: rowData.gridId, gridId: rowData.gridId,
blockId: rowData.blockId, blockId: rowData.blockId,
rowId: rowData.rowId, rowId: rowData.id,
), ),
super(RowActionSheetState.initial(rowData)) { super(RowActionSheetState.initial(rowData)) {
on<RowActionSheetEvent>( on<RowActionSheetEvent>(
@ -53,10 +53,10 @@ class RowActionSheetEvent with _$RowActionSheetEvent {
@freezed @freezed
class RowActionSheetState with _$RowActionSheetState { class RowActionSheetState with _$RowActionSheetState {
const factory RowActionSheetState({ const factory RowActionSheetState({
required GridRow rowData, required GridRowInfo rowData,
}) = _RowActionSheetState; }) = _RowActionSheetState;
factory RowActionSheetState.initial(GridRow rowData) => RowActionSheetState( factory RowActionSheetState.initial(GridRowInfo rowData) => RowActionSheetState(
rowData: rowData, rowData: rowData,
); );
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -10,7 +10,7 @@ part 'property_bloc.freezed.dart';
class GridPropertyBloc extends Bloc<GridPropertyEvent, GridPropertyState> { class GridPropertyBloc extends Bloc<GridPropertyEvent, GridPropertyState> {
final GridFieldCache _fieldCache; final GridFieldCache _fieldCache;
Function(List<Field>)? _onFieldsFn; Function(List<GridFieldPB>)? _onFieldsFn;
GridPropertyBloc({required String gridId, required GridFieldCache fieldCache}) GridPropertyBloc({required String gridId, required GridFieldCache fieldCache})
: _fieldCache = fieldCache, : _fieldCache = fieldCache,
@ -62,7 +62,7 @@ class GridPropertyBloc extends Bloc<GridPropertyEvent, GridPropertyState> {
class GridPropertyEvent with _$GridPropertyEvent { class GridPropertyEvent with _$GridPropertyEvent {
const factory GridPropertyEvent.initial() = _Initial; const factory GridPropertyEvent.initial() = _Initial;
const factory GridPropertyEvent.setFieldVisibility(String fieldId, bool visibility) = _SetFieldVisibility; 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; const factory GridPropertyEvent.moveField(int fromIndex, int toIndex) = _MoveField;
} }
@ -70,10 +70,10 @@ class GridPropertyEvent with _$GridPropertyEvent {
class GridPropertyState with _$GridPropertyState { class GridPropertyState with _$GridPropertyState {
const factory GridPropertyState({ const factory GridPropertyState({
required String gridId, required String gridId,
required List<Field> fields, required List<GridFieldPB> fields,
}) = _GridPropertyState; }) = _GridPropertyState;
factory GridPropertyState.initial(String gridId, List<Field> fields) => GridPropertyState( factory GridPropertyState.initial(String gridId, List<GridFieldPB> fields) => GridPropertyState(
gridId: gridId, gridId: gridId,
fields: fields, 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/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.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-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:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
@ -13,45 +13,51 @@ part 'home_bloc.freezed.dart';
class HomeBloc extends Bloc<HomeEvent, HomeState> { class HomeBloc extends Bloc<HomeEvent, HomeState> {
final UserWorkspaceListener _listener; final UserWorkspaceListener _listener;
HomeBloc(UserProfile user, CurrentWorkspaceSetting workspaceSetting) HomeBloc(UserProfilePB user, CurrentWorkspaceSettingPB workspaceSetting)
: _listener = UserWorkspaceListener(userProfile: user), : _listener = UserWorkspaceListener(userProfile: user),
super(HomeState.initial(workspaceSetting)) { super(HomeState.initial(workspaceSetting)) {
on<HomeEvent>((event, emit) async { on<HomeEvent>(
await event.map( (event, emit) async {
initial: (_Initial value) { await event.map(
_listener.start( initial: (_Initial value) {
onAuthChanged: (result) => _authDidChanged(result), _listener.start(
onSettingUpdated: (result) { onAuthChanged: (result) => _authDidChanged(result),
result.fold( onSettingUpdated: (result) {
(setting) => add(HomeEvent.didReceiveWorkspaceSetting(setting)), result.fold(
(r) => Log.error(r), (setting) => add(HomeEvent.didReceiveWorkspaceSetting(setting)),
); (r) => Log.error(r),
}, );
); },
}, );
showLoading: (e) async { },
emit(state.copyWith(isLoading: e.isLoading)); showLoading: (e) async {
}, emit(state.copyWith(isLoading: e.isLoading));
setEditPannel: (e) async { },
emit(state.copyWith(pannelContext: some(e.editContext))); setEditPannel: (e) async {
}, emit(state.copyWith(pannelContext: some(e.editContext)));
dismissEditPannel: (value) async { },
emit(state.copyWith(pannelContext: none())); dismissEditPannel: (value) async {
}, emit(state.copyWith(pannelContext: none()));
forceCollapse: (e) async { },
emit(state.copyWith(forceCollapse: e.forceCollapse)); forceCollapse: (e) async {
}, emit(state.copyWith(forceCollapse: e.forceCollapse));
didReceiveWorkspaceSetting: (_DidReceiveWorkspaceSetting value) { },
emit(state.copyWith(workspaceSetting: value.setting)); didReceiveWorkspaceSetting: (_DidReceiveWorkspaceSetting value) {
}, emit(state.copyWith(workspaceSetting: value.setting));
unauthorized: (_Unauthorized value) { },
emit(state.copyWith(unauthorized: true)); unauthorized: (_Unauthorized value) {
}, emit(state.copyWith(unauthorized: true));
collapseMenu: (e) { },
emit(state.copyWith(isMenuCollapsed: !state.isMenuCollapsed)); 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 @override
@ -76,9 +82,10 @@ class HomeEvent with _$HomeEvent {
const factory HomeEvent.forceCollapse(bool forceCollapse) = _ForceCollapse; const factory HomeEvent.forceCollapse(bool forceCollapse) = _ForceCollapse;
const factory HomeEvent.setEditPannel(EditPannelContext editContext) = _ShowEditPannel; const factory HomeEvent.setEditPannel(EditPannelContext editContext) = _ShowEditPannel;
const factory HomeEvent.dismissEditPannel() = _DismissEditPannel; 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.unauthorized(String msg) = _Unauthorized;
const factory HomeEvent.collapseMenu() = _CollapseMenu; const factory HomeEvent.collapseMenu() = _CollapseMenu;
const factory HomeEvent.editPannelResized(double offset) = _EditPannelResized;
} }
@freezed @freezed
@ -87,17 +94,19 @@ class HomeState with _$HomeState {
required bool isLoading, required bool isLoading,
required bool forceCollapse, required bool forceCollapse,
required Option<EditPannelContext> pannelContext, required Option<EditPannelContext> pannelContext,
required CurrentWorkspaceSetting workspaceSetting, required CurrentWorkspaceSettingPB workspaceSetting,
required bool unauthorized, required bool unauthorized,
required bool isMenuCollapsed, required bool isMenuCollapsed,
required double resizeOffset,
}) = _HomeState; }) = _HomeState;
factory HomeState.initial(CurrentWorkspaceSetting workspaceSetting) => HomeState( factory HomeState.initial(CurrentWorkspaceSettingPB workspaceSetting) => HomeState(
isLoading: false, isLoading: false,
forceCollapse: false, forceCollapse: false,
pannelContext: none(), pannelContext: none(),
workspaceSetting: workspaceSetting, workspaceSetting: workspaceSetting,
unauthorized: false, unauthorized: false,
isMenuCollapsed: false, isMenuCollapsed: false,
resizeOffset: 0,
); );
} }

View file

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

View file

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

View file

@ -62,7 +62,7 @@ class ViewSectionBloc extends Bloc<ViewSectionEvent, ViewSectionState> {
Future<void> _moveView(_MoveView value, Emitter<ViewSectionState> emit) async { Future<void> _moveView(_MoveView value, Emitter<ViewSectionState> emit) async {
if (value.fromIndex < state.views.length) { if (value.fromIndex < state.views.length) {
final viewId = state.views[value.fromIndex].id; 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)); views.insert(value.toIndex, views.removeAt(value.fromIndex));
emit(state.copyWith(views: views)); emit(state.copyWith(views: views));
@ -92,16 +92,16 @@ class ViewSectionBloc extends Bloc<ViewSectionEvent, ViewSectionState> {
@freezed @freezed
class ViewSectionEvent with _$ViewSectionEvent { class ViewSectionEvent with _$ViewSectionEvent {
const factory ViewSectionEvent.initial() = _Initial; 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.moveView(int fromIndex, int toIndex) = _MoveView;
const factory ViewSectionEvent.didReceiveViewUpdated(List<View> views) = _DidReceiveViewUpdated; const factory ViewSectionEvent.didReceiveViewUpdated(List<ViewPB> views) = _DidReceiveViewUpdated;
} }
@freezed @freezed
class ViewSectionState with _$ViewSectionState { class ViewSectionState with _$ViewSectionState {
const factory ViewSectionState({ const factory ViewSectionState({
required List<View> views, required List<ViewPB> views,
View? selectedView, ViewPB? selectedView,
}) = _ViewSectionState; }) = _ViewSectionState;
factory ViewSectionState.initial(AppViewDataContext appViewData) => 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( trashOrFailed.fold(
(trash) { (trash) {
add(TrashEvent.didReceiveTrash(trash)); add(TrashEvent.didReceiveTrash(trash));
@ -66,9 +66,9 @@ class TrashBloc extends Bloc<TrashEvent, TrashState> {
@freezed @freezed
class TrashEvent with _$TrashEvent { class TrashEvent with _$TrashEvent {
const factory TrashEvent.initial() = Initial; 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.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.restoreAll() = RestoreAll;
const factory TrashEvent.deleteAll() = DeleteAll; const factory TrashEvent.deleteAll() = DeleteAll;
} }
@ -76,7 +76,7 @@ class TrashEvent with _$TrashEvent {
@freezed @freezed
class TrashState with _$TrashState { class TrashState with _$TrashState {
const factory TrashState({ const factory TrashState({
required List<Trash> objects, required List<TrashPB> objects,
required Either<Unit, FlowyError> successOrFailure, required Either<Unit, FlowyError> successOrFailure,
}) = _TrashState; }) = _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/protobuf/flowy-folder/trash.pb.dart';
import 'package:flowy_sdk/rust_stream.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 { class TrashListener {
StreamSubscription<SubscribeObject>? _subscription; StreamSubscription<SubscribeObject>? _subscription;
@ -27,7 +27,7 @@ class TrashListener {
if (_trashUpdated != null) { if (_trashUpdated != null) {
result.fold( result.fold(
(payload) { (payload) {
final repeatedTrash = RepeatedTrash.fromBuffer(payload); final repeatedTrash = RepeatedTrashPB.fromBuffer(payload);
_trashUpdated!(left(repeatedTrash.items)); _trashUpdated!(left(repeatedTrash.items));
}, },
(error) => _trashUpdated!(right(error)), (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'; import 'package:flowy_sdk/protobuf/flowy-folder/trash.pb.dart';
class TrashService { class TrashService {
Future<Either<RepeatedTrash, FlowyError>> readTrash() { Future<Either<RepeatedTrashPB, FlowyError>> readTrash() {
return FolderEventReadTrash().send(); return FolderEventReadTrash().send();
} }
Future<Either<Unit, FlowyError>> putback(String trashId) { Future<Either<Unit, FlowyError>> putback(String trashId) {
final id = TrashId.create()..id = trashId; final id = TrashIdPB.create()..id = trashId;
return FolderEventPutbackTrash(id).send(); return FolderEventPutbackTrash(id).send();
} }
Future<Either<Unit, FlowyError>> deleteViews(List<Tuple2<String, TrashType>> trashList) { Future<Either<Unit, FlowyError>> deleteViews(List<Tuple2<String, TrashType>> trashList) {
final items = trashList.map((trash) { final items = trashList.map((trash) {
return TrashId.create() return TrashIdPB.create()
..id = trash.value1 ..id = trash.value1
..ty = trash.value2; ..ty = trash.value2;
}); });
final ids = RepeatedTrashId(items: items); final ids = RepeatedTrashIdPB(items: items);
return FolderEventDeleteTrash(ids).send(); return FolderEventDeleteTrash(ids).send();
} }

View file

@ -11,7 +11,7 @@ part 'view_bloc.freezed.dart';
class ViewBloc extends Bloc<ViewEvent, ViewState> { class ViewBloc extends Bloc<ViewEvent, ViewState> {
final ViewService service; final ViewService service;
final ViewListener listener; final ViewListener listener;
final View view; final ViewPB view;
ViewBloc({ ViewBloc({
required this.view, required this.view,
@ -81,18 +81,18 @@ class ViewEvent with _$ViewEvent {
const factory ViewEvent.rename(String newName) = Rename; const factory ViewEvent.rename(String newName) = Rename;
const factory ViewEvent.delete() = Delete; const factory ViewEvent.delete() = Delete;
const factory ViewEvent.duplicate() = Duplicate; const factory ViewEvent.duplicate() = Duplicate;
const factory ViewEvent.viewDidUpdate(Either<View, FlowyError> result) = ViewDidUpdate; const factory ViewEvent.viewDidUpdate(Either<ViewPB, FlowyError> result) = ViewDidUpdate;
} }
@freezed @freezed
class ViewState with _$ViewState { class ViewState with _$ViewState {
const factory ViewState({ const factory ViewState({
required View view, required ViewPB view,
required bool isEditing, required bool isEditing,
required Either<Unit, FlowyError> successOrFailure, required Either<Unit, FlowyError> successOrFailure,
}) = _ViewState; }) = _ViewState;
factory ViewState.init(View view) => ViewState( factory ViewState.init(ViewPB view) => ViewState(
view: view, view: view,
isEditing: false, isEditing: false,
successOrFailure: left(unit), 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}) { Widget renderThumbnail({Color? iconColor}) {
String thumbnail = "file_icon"; 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_sdk/rust_stream.dart';
import 'package:flowy_infra/notifier.dart'; import 'package:flowy_infra/notifier.dart';
typedef DeleteViewNotifyValue = Either<View, FlowyError>; typedef DeleteViewNotifyValue = Either<ViewPB, FlowyError>;
typedef UpdateViewNotifiedValue = Either<View, FlowyError>; typedef UpdateViewNotifiedValue = Either<ViewPB, FlowyError>;
typedef RestoreViewNotifiedValue = Either<View, FlowyError>; typedef RestoreViewNotifiedValue = Either<ViewPB, FlowyError>;
class ViewListener { class ViewListener {
StreamSubscription<SubscribeObject>? _subscription; StreamSubscription<SubscribeObject>? _subscription;
@ -19,7 +19,7 @@ class ViewListener {
final PublishNotifier<DeleteViewNotifyValue> _deletedNotifier = PublishNotifier(); final PublishNotifier<DeleteViewNotifyValue> _deletedNotifier = PublishNotifier();
final PublishNotifier<RestoreViewNotifiedValue> _restoredNotifier = PublishNotifier(); final PublishNotifier<RestoreViewNotifiedValue> _restoredNotifier = PublishNotifier();
FolderNotificationParser? _parser; FolderNotificationParser? _parser;
View view; ViewPB view;
ViewListener({ ViewListener({
required this.view, required this.view,
@ -62,19 +62,19 @@ class ViewListener {
switch (ty) { switch (ty) {
case FolderNotification.ViewUpdated: case FolderNotification.ViewUpdated:
result.fold( result.fold(
(payload) => _updatedViewNotifier.value = left(View.fromBuffer(payload)), (payload) => _updatedViewNotifier.value = left(ViewPB.fromBuffer(payload)),
(error) => _updatedViewNotifier.value = right(error), (error) => _updatedViewNotifier.value = right(error),
); );
break; break;
case FolderNotification.ViewDeleted: case FolderNotification.ViewDeleted:
result.fold( result.fold(
(payload) => _deletedNotifier.value = left(View.fromBuffer(payload)), (payload) => _deletedNotifier.value = left(ViewPB.fromBuffer(payload)),
(error) => _deletedNotifier.value = right(error), (error) => _deletedNotifier.value = right(error),
); );
break; break;
case FolderNotification.ViewRestored: case FolderNotification.ViewRestored:
result.fold( result.fold(
(payload) => _restoredNotifier.value = left(View.fromBuffer(payload)), (payload) => _restoredNotifier.value = left(ViewPB.fromBuffer(payload)),
(error) => _restoredNotifier.value = right(error), (error) => _restoredNotifier.value = right(error),
); );
break; 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'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
class ViewService { class ViewService {
Future<Either<View, FlowyError>> readView({required String viewId}) { Future<Either<ViewPB, FlowyError>> readView({required String viewId}) {
final request = ViewId(value: viewId); final request = ViewIdPB(value: viewId);
return FolderEventReadView(request).send(); return FolderEventReadView(request).send();
} }
Future<Either<View, FlowyError>> updateView({required String viewId, String? name, String? desc}) { Future<Either<ViewPB, FlowyError>> updateView({required String viewId, String? name, String? desc}) {
final request = UpdateViewPayload.create()..viewId = viewId; final request = UpdateViewPayloadPB.create()..viewId = viewId;
if (name != null) { if (name != null) {
request.name = name; request.name = name;
@ -25,12 +25,12 @@ class ViewService {
} }
Future<Either<Unit, FlowyError>> delete({required String viewId}) { 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(); return FolderEventDeleteView(request).send();
} }
Future<Either<Unit, FlowyError>> duplicate({required String viewId}) { Future<Either<Unit, FlowyError>> duplicate({required String viewId}) {
final request = ViewId(value: viewId); final request = ViewIdPB(value: viewId);
return FolderEventDuplicateView(request).send(); 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); final result = await userService.openWorkspace(workspace.id);
emit(result.fold( emit(result.fold(
(workspaces) => state.copyWith(successOrFailure: left(unit)), (workspaces) => state.copyWith(successOrFailure: left(unit)),
@ -82,8 +82,8 @@ class WelcomeEvent with _$WelcomeEvent {
const factory WelcomeEvent.initial() = Initial; const factory WelcomeEvent.initial() = Initial;
// const factory WelcomeEvent.fetchWorkspaces() = FetchWorkspace; // const factory WelcomeEvent.fetchWorkspaces() = FetchWorkspace;
const factory WelcomeEvent.createWorkspace(String name, String desc) = CreateWorkspace; const factory WelcomeEvent.createWorkspace(String name, String desc) = CreateWorkspace;
const factory WelcomeEvent.openWorkspace(Workspace workspace) = OpenWorkspace; const factory WelcomeEvent.openWorkspace(WorkspacePB workspace) = OpenWorkspace;
const factory WelcomeEvent.workspacesReveived(Either<List<Workspace>, FlowyError> workspacesOrFail) = const factory WelcomeEvent.workspacesReveived(Either<List<WorkspacePB>, FlowyError> workspacesOrFail) =
WorkspacesReceived; WorkspacesReceived;
} }
@ -91,7 +91,7 @@ class WelcomeEvent with _$WelcomeEvent {
class WelcomeState with _$WelcomeState { class WelcomeState with _$WelcomeState {
const factory WelcomeState({ const factory WelcomeState({
required bool isLoading, required bool isLoading,
required List<Workspace> workspaces, required List<WorkspacePB> workspaces,
required Either<Unit, FlowyError> successOrFailure, required Either<Unit, FlowyError> successOrFailure,
}) = _WelcomeState; }) = _WelcomeState;

View file

@ -3,21 +3,21 @@ import 'dart:typed_data';
import 'package:app_flowy/core/folder_notification.dart'; import 'package:app_flowy/core/folder_notification.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:flowy_infra/notifier.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/app.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/workspace.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-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/dart_notification.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder/dart_notification.pb.dart';
typedef AppListNotifyValue = Either<List<App>, FlowyError>; typedef AppListNotifyValue = Either<List<AppPB>, FlowyError>;
typedef WorkspaceNotifyValue = Either<Workspace, FlowyError>; typedef WorkspaceNotifyValue = Either<WorkspacePB, FlowyError>;
class WorkspaceListener { class WorkspaceListener {
PublishNotifier<AppListNotifyValue>? _appsChangedNotifier = PublishNotifier(); PublishNotifier<AppListNotifyValue>? _appsChangedNotifier = PublishNotifier();
PublishNotifier<WorkspaceNotifyValue>? _workspaceUpdatedNotifier = PublishNotifier(); PublishNotifier<WorkspaceNotifyValue>? _workspaceUpdatedNotifier = PublishNotifier();
FolderNotificationListener? _listener; FolderNotificationListener? _listener;
final UserProfile user; final UserProfilePB user;
final String workspaceId; final String workspaceId;
WorkspaceListener({ WorkspaceListener({
@ -47,13 +47,13 @@ class WorkspaceListener {
switch (ty) { switch (ty) {
case FolderNotification.WorkspaceUpdated: case FolderNotification.WorkspaceUpdated:
result.fold( result.fold(
(payload) => _workspaceUpdatedNotifier?.value = left(Workspace.fromBuffer(payload)), (payload) => _workspaceUpdatedNotifier?.value = left(WorkspacePB.fromBuffer(payload)),
(error) => _workspaceUpdatedNotifier?.value = right(error), (error) => _workspaceUpdatedNotifier?.value = right(error),
); );
break; break;
case FolderNotification.WorkspaceAppsChanged: case FolderNotification.WorkspaceAppsChanged:
result.fold( result.fold(
(payload) => _appsChangedNotifier?.value = left(RepeatedApp.fromBuffer(payload).items), (payload) => _appsChangedNotifier?.value = left(RepeatedAppPB.fromBuffer(payload).items),
(error) => _appsChangedNotifier?.value = right(error), (error) => _appsChangedNotifier?.value = right(error),
); );
break; 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/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.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/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:flowy_sdk/protobuf/flowy-folder/workspace.pb.dart';
import 'package:app_flowy/generated/locale_keys.g.dart'; import 'package:app_flowy/generated/locale_keys.g.dart';
@ -15,16 +15,16 @@ class WorkspaceService {
WorkspaceService({ WorkspaceService({
required this.workspaceId, required this.workspaceId,
}); });
Future<Either<App, FlowyError>> createApp({required String name, required String desc}) { Future<Either<AppPB, FlowyError>> createApp({required String name, required String desc}) {
final payload = CreateAppPayload.create() final payload = CreateAppPayloadPB.create()
..name = name ..name = name
..workspaceId = workspaceId ..workspaceId = workspaceId
..desc = desc; ..desc = desc;
return FolderEventCreateApp(payload).send(); return FolderEventCreateApp(payload).send();
} }
Future<Either<Workspace, FlowyError>> getWorkspace() { Future<Either<WorkspacePB, FlowyError>> getWorkspace() {
final payload = WorkspaceId.create()..value = workspaceId; final payload = WorkspaceIdPB.create()..value = workspaceId;
return FolderEventReadWorkspaces(payload).send().then((result) { return FolderEventReadWorkspaces(payload).send().then((result) {
return result.fold( return result.fold(
(workspaces) { (workspaces) {
@ -41,8 +41,8 @@ class WorkspaceService {
}); });
} }
Future<Either<List<App>, FlowyError>> getApps() { Future<Either<List<AppPB>, FlowyError>> getApps() {
final payload = WorkspaceId.create()..value = workspaceId; final payload = WorkspaceIdPB.create()..value = workspaceId;
return FolderEventReadWorkspaceApps(payload).send().then((result) { return FolderEventReadWorkspaceApps(payload).send().then((result) {
return result.fold( return result.fold(
(apps) => left(apps.items), (apps) => left(apps.items),
@ -56,7 +56,7 @@ class WorkspaceService {
required int fromIndex, required int fromIndex,
required int toIndex, required int toIndex,
}) { }) {
final payload = MoveFolderItemPayload.create() final payload = MoveFolderItemPayloadPB.create()
..itemId = appId ..itemId = appId
..from = fromIndex ..from = fromIndex
..to = toIndex ..to = toIndex

View file

@ -27,6 +27,8 @@ class HomeLayout {
menuWidth = Sizes.sideBarLg; menuWidth = Sizes.sideBarLg;
} }
menuWidth += homeBlocState.resizeOffset;
if (forceCollapse) { if (forceCollapse) {
showMenu = false; showMenu = false;
} else { } 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:app_flowy/startup/startup.dart';
import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/log.dart';
import 'package:flowy_infra_ui/style_widget/container.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:flowy_sdk/protobuf/flowy-folder/protobuf.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
@ -18,8 +19,8 @@ import 'home_stack.dart';
import 'menu/menu.dart'; import 'menu/menu.dart';
class HomeScreen extends StatefulWidget { class HomeScreen extends StatefulWidget {
final UserProfile user; final UserProfilePB user;
final CurrentWorkspaceSetting workspaceSetting; final CurrentWorkspaceSettingPB workspaceSetting;
const HomeScreen(this.user, this.workspaceSetting, {Key? key}) : super(key: key); const HomeScreen(this.user, this.workspaceSetting, {Key? key}) : super(key: key);
@override @override
@ -27,7 +28,7 @@ class HomeScreen extends StatefulWidget {
} }
class _HomeScreenState extends State<HomeScreen> { class _HomeScreenState extends State<HomeScreen> {
View? initialView; ViewPB? initialView;
@override @override
void initState() { void initState() {
@ -87,6 +88,7 @@ class _HomeScreenState extends State<HomeScreen> {
context: context, context: context,
state: state, state: state,
); );
final homeMenuResizer = _buildHomeMenuResizer(context: context);
final editPannel = _buildEditPannel( final editPannel = _buildEditPannel(
homeState: state, homeState: state,
layout: layout, layout: layout,
@ -99,6 +101,7 @@ class _HomeScreenState extends State<HomeScreen> {
homeMenu: menu, homeMenu: menu,
editPannel: editPannel, editPannel: editPannel,
bubble: bubble, bubble: bubble,
homeMenuResizer: homeMenuResizer,
); );
}, },
); );
@ -122,7 +125,10 @@ class _HomeScreenState extends State<HomeScreen> {
); );
final latestView = workspaceSetting.hasLatestView() ? workspaceSetting.latestView : null; 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)); 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({ Widget _layoutWidgets({
required HomeLayout layout, required HomeLayout layout,
required Widget homeMenu, required Widget homeMenu,
required Widget homeStack, required Widget homeStack,
required Widget editPannel, required Widget editPannel,
required Widget bubble, required Widget bubble,
required Widget homeMenuResizer,
}) { }) {
return Stack( return Stack(
children: [ children: [
@ -167,6 +192,7 @@ class _HomeScreenState extends State<HomeScreen> {
.constrained(minWidth: 500) .constrained(minWidth: 500)
.positioned(left: layout.homePageLOffset, right: layout.homePageROffset, bottom: 0, top: 0, animate: true) .positioned(left: layout.homePageLOffset, right: layout.homePageROffset, bottom: 0, top: 0, animate: true)
.animate(layout.animDuration, Curves.easeOut), .animate(layout.animDuration, Curves.easeOut),
homeMenuResizer.positioned(left: layout.homePageLOffset - 5),
bubble bubble
.positioned( .positioned(
right: 20, right: 20,

View file

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

View file

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

View file

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

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