diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 527b8acd31..7b7c889a72 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -3,11 +3,11 @@ name: CI
on:
push:
branches:
- - 'main'
-
+ - "main"
+
pull_request:
branches:
- - 'main'
+ - "main"
jobs:
build:
@@ -23,36 +23,37 @@ jobs:
steps:
- uses: actions/checkout@v2
-
+
- id: rust_toolchain
uses: actions-rs/toolchain@v1
with:
- toolchain: 'stable-2022-01-20'
-
+ toolchain: "stable-2022-01-20"
+
- id: flutter
uses: subosito/flutter-action@v2
with:
- channel: 'stable'
+ channel: "stable"
cache: true
- flutter-version: '3.0.5'
+ flutter-version: "3.0.5"
- name: Cache Cargo
+ id: cache-cargo
uses: actions/cache@v2
- with:
+ with:
path: |
~/.cargo
key: ${{ runner.os }}-cargo-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
- name: Cache Rust
uses: actions/cache@v2
- with:
+ with:
path: |
frontend/rust-lib/target
shared-lib/target
- key: ${{ runner.os }}-rust-rust-lib-share-lib-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
+ key: ${{ runner.os }}-rust-rust-lib-share-lib-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
- name: Setup Environment
- run: |
+ run: |
if [ "$RUNNER_OS" == "Linux" ]; then
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
@@ -64,11 +65,16 @@ jobs:
fi
shell: bash
- - name: Deps
+ - if: steps.cache-cargo.outputs.cache-hit != 'true'
+ name: Deps
working-directory: frontend
run: |
cargo install cargo-make
cargo install duckscript_cli
+
+ - name: Cargo make flowy_dev
+ working-directory: frontend
+ run: |
cargo make flowy_dev
- name: Config Flutter
diff --git a/.github/workflows/dart_lint.yml b/.github/workflows/dart_lint.yml
index 3ff87dc680..251848fb31 100644
--- a/.github/workflows/dart_lint.yml
+++ b/.github/workflows/dart_lint.yml
@@ -7,14 +7,14 @@ name: Flutter lint
on:
push:
- branches: [ main ]
+ branches: [main]
pull_request:
- branches: [ main ]
+ branches: [main]
env:
CARGO_TERM_COLOR: always
-jobs:
+jobs:
flutter-analyze:
name: flutter analyze
runs-on: ubuntu-latest
@@ -23,16 +23,38 @@ jobs:
uses: actions/checkout@v2
- uses: subosito/flutter-action@v1
with:
- flutter-version: '3.0.5'
+ flutter-version: "3.0.5"
channel: "stable"
- uses: actions-rs/toolchain@v1
with:
- toolchain: 'stable-2022-01-20'
+ toolchain: "stable-2022-01-20"
- - name: Rust Deps
+ - name: Cache Cargo
+ id: cache-cargo
+ uses: actions/cache@v2
+ with:
+ path: |
+ ~/.cargo
+ key: ${{ runner.os }}-cargo-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
+
+ - name: Cache Rust
+ id: cache-rust-target
+ uses: actions/cache@v2
+ with:
+ path: |
+ frontend/rust-lib/target
+ shared-lib/target
+ key: ${{ runner.os }}-rust-rust-lib-share-lib-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
+
+ - if: steps.cache-cargo.outputs.cache-hit != 'true'
+ name: Rust Deps
working-directory: frontend
run: |
cargo install cargo-make
+
+ - name: Cargo make flowy dev
+ working-directory: frontend
+ run: |
cargo make flowy_dev
- name: Flutter Deps
@@ -53,4 +75,3 @@ jobs:
- name: Run Flutter Analyzer
working-directory: frontend/app_flowy
run: flutter analyze
-
diff --git a/.github/workflows/dart_test.yml b/.github/workflows/dart_test.yml
index e2a858999c..33cbeb1a3a 100644
--- a/.github/workflows/dart_test.yml
+++ b/.github/workflows/dart_test.yml
@@ -3,12 +3,12 @@ name: Unit test(Flutter)
on:
push:
branches:
- - 'main'
-
+ - "main"
+
pull_request:
branches:
- - 'main'
- - 'feat/flowy_editor'
+ - "main"
+ - "feat/flowy_editor"
env:
CARGO_TERM_COLOR: always
@@ -18,42 +18,49 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
-
+
- uses: actions-rs/toolchain@v1
with:
- toolchain: 'stable-2022-01-20'
-
+ toolchain: "stable-2022-01-20"
+
- uses: subosito/flutter-action@v2
with:
- channel: 'stable'
- flutter-version: '3.0.5'
+ channel: "stable"
+ flutter-version: "3.0.5"
cache: true
- name: Cache Cargo
uses: actions/cache@v2
- with:
+ with:
path: |
~/.cargo
key: ${{ runner.os }}-cargo-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
- name: Cache Rust
+ id: cache-rust-target
uses: actions/cache@v2
- with:
+ with:
path: |
frontend/rust-lib/target
shared-lib/target
- key: ${{ runner.os }}-rust-rust-lib-share-lib-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
+ key: ${{ runner.os }}-rust-rust-lib-share-lib-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
+
+ - if: steps.cache-cargo.outputs.cache-hit != 'true'
+ name: Rust Deps
+ working-directory: frontend
+ run: |
+ cargo install cargo-make
+
+ - name: Cargo make flowy dev
+ working-directory: frontend
+ run: |
+ cargo make flowy_dev
- name: Flutter Deps
working-directory: frontend/app_flowy
run: |
flutter config --enable-linux-desktop
-
- - name: Rust Deps
- working-directory: frontend
- run: |
- cargo install cargo-make
- cargo make flowy_dev
+
- name: Build FlowySDK
working-directory: frontend
run: |
@@ -65,15 +72,9 @@ jobs:
flutter packages pub get
flutter packages pub run easy_localization:generate -f keys -o locale_keys.g.dart -S assets/translations -s en.json
flutter packages pub run build_runner build --delete-conflicting-outputs
-
+
- name: Run bloc tests
working-directory: frontend/app_flowy
run: |
flutter pub get
flutter test
-
- - name: Run FlowyEditor tests
- working-directory: frontend/app_flowy/packages/flowy_editor
- run: |
- flutter pub get
- flutter test
\ No newline at end of file
diff --git a/.github/workflows/flowy_editor_test.yml b/.github/workflows/flowy_editor_test.yml
new file mode 100644
index 0000000000..1bff599ace
--- /dev/null
+++ b/.github/workflows/flowy_editor_test.yml
@@ -0,0 +1,36 @@
+name: FlowyEditor test
+
+on:
+ push:
+ branches:
+ - "main"
+
+ pull_request:
+ branches:
+ - "main"
+
+env:
+ CARGO_TERM_COLOR: always
+
+jobs:
+ tests:
+ strategy:
+ matrix:
+ os: [macos-latest, ubuntu-latest, windows-latest]
+
+ runs-on: ${{ matrix.os }}
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - uses: subosito/flutter-action@v2
+ with:
+ channel: "stable"
+ flutter-version: "3.0.5"
+ cache: true
+
+ - name: Run FlowyEditor tests
+ working-directory: frontend/app_flowy/packages/appflowy_editor
+ run: |
+ flutter pub get
+ flutter test
diff --git a/frontend/.vscode/launch.json b/frontend/.vscode/launch.json
index 036b5ab35c..e70b9ffb97 100644
--- a/frontend/.vscode/launch.json
+++ b/frontend/.vscode/launch.json
@@ -29,7 +29,7 @@
"program": "./lib/main.dart",
"type": "dart",
"env": {
- "RUST_LOG": "debug"
+ "RUST_LOG": "trace"
},
"cwd": "${workspaceRoot}/app_flowy"
},
@@ -44,7 +44,7 @@
"type": "dart",
"preLaunchTask": "AF: Clean + Rebuild All",
"env": {
- "RUST_LOG": "info"
+ "RUST_LOG": "trace"
},
"cwd": "${workspaceRoot}/app_flowy"
},
diff --git a/frontend/app_flowy/analysis_options.yaml b/frontend/app_flowy/analysis_options.yaml
index 5717d49105..e345c3da76 100644
--- a/frontend/app_flowy/analysis_options.yaml
+++ b/frontend/app_flowy/analysis_options.yaml
@@ -14,7 +14,7 @@ analyzer:
exclude:
- "**/*.g.dart"
- "**/*.freezed.dart"
- - "packages/flowy_editor/**"
+ - "packages/appflowy_editor/**"
- "packages/editor/**"
# - "packages/flowy_infra_ui/**"
linter:
diff --git a/frontend/app_flowy/assets/images/emoji/1F42F.svg b/frontend/app_flowy/assets/images/emoji/1F42F.svg
new file mode 100644
index 0000000000..a6e8e3e81f
--- /dev/null
+++ b/frontend/app_flowy/assets/images/emoji/1F42F.svg
@@ -0,0 +1,32 @@
+
diff --git a/frontend/app_flowy/assets/images/emoji/1F431.svg b/frontend/app_flowy/assets/images/emoji/1F431.svg
new file mode 100644
index 0000000000..26aa279abc
--- /dev/null
+++ b/frontend/app_flowy/assets/images/emoji/1F431.svg
@@ -0,0 +1,24 @@
+
diff --git a/frontend/app_flowy/assets/images/emoji/1F435.svg b/frontend/app_flowy/assets/images/emoji/1F435.svg
new file mode 100644
index 0000000000..0220a6e58e
--- /dev/null
+++ b/frontend/app_flowy/assets/images/emoji/1F435.svg
@@ -0,0 +1,28 @@
+
diff --git a/frontend/app_flowy/assets/images/emoji/1F43A.svg b/frontend/app_flowy/assets/images/emoji/1F43A.svg
new file mode 100644
index 0000000000..3e29b3a6a9
--- /dev/null
+++ b/frontend/app_flowy/assets/images/emoji/1F43A.svg
@@ -0,0 +1,28 @@
+
diff --git a/frontend/app_flowy/assets/images/emoji/1F600.svg b/frontend/app_flowy/assets/images/emoji/1F600.svg
new file mode 100644
index 0000000000..e9e1d0ea88
--- /dev/null
+++ b/frontend/app_flowy/assets/images/emoji/1F600.svg
@@ -0,0 +1,17 @@
+
diff --git a/frontend/app_flowy/assets/images/emoji/1F984.svg b/frontend/app_flowy/assets/images/emoji/1F984.svg
new file mode 100644
index 0000000000..a5f8206cbc
--- /dev/null
+++ b/frontend/app_flowy/assets/images/emoji/1F984.svg
@@ -0,0 +1,21 @@
+
diff --git a/frontend/app_flowy/assets/images/emoji/1F9CC.svg b/frontend/app_flowy/assets/images/emoji/1F9CC.svg
new file mode 100644
index 0000000000..eb30038228
--- /dev/null
+++ b/frontend/app_flowy/assets/images/emoji/1F9CC.svg
@@ -0,0 +1,33 @@
+
diff --git a/frontend/app_flowy/assets/images/emoji/1F9DB.svg b/frontend/app_flowy/assets/images/emoji/1F9DB.svg
new file mode 100644
index 0000000000..590829d25c
--- /dev/null
+++ b/frontend/app_flowy/assets/images/emoji/1F9DB.svg
@@ -0,0 +1,26 @@
+
diff --git a/frontend/app_flowy/assets/images/emoji/1F9DD-200D-2642-FE0F.svg b/frontend/app_flowy/assets/images/emoji/1F9DD-200D-2642-FE0F.svg
new file mode 100644
index 0000000000..62e5101f53
--- /dev/null
+++ b/frontend/app_flowy/assets/images/emoji/1F9DD-200D-2642-FE0F.svg
@@ -0,0 +1,39 @@
+
diff --git a/frontend/app_flowy/assets/images/emoji/1F9DE-200D-2642-FE0F.svg b/frontend/app_flowy/assets/images/emoji/1F9DE-200D-2642-FE0F.svg
new file mode 100644
index 0000000000..e662de70a6
--- /dev/null
+++ b/frontend/app_flowy/assets/images/emoji/1F9DE-200D-2642-FE0F.svg
@@ -0,0 +1,28 @@
+
diff --git a/frontend/app_flowy/assets/images/emoji/1F9DF.svg b/frontend/app_flowy/assets/images/emoji/1F9DF.svg
new file mode 100644
index 0000000000..e2ea11f33a
--- /dev/null
+++ b/frontend/app_flowy/assets/images/emoji/1F9DF.svg
@@ -0,0 +1,28 @@
+
diff --git a/frontend/app_flowy/lib/plugins/blank/blank.dart b/frontend/app_flowy/lib/plugins/blank/blank.dart
index 11f779eeb0..350a30ed50 100644
--- a/frontend/app_flowy/lib/plugins/blank/blank.dart
+++ b/frontend/app_flowy/lib/plugins/blank/blank.dart
@@ -16,7 +16,7 @@ class BlankPluginBuilder extends PluginBuilder {
String get menuName => "Blank";
@override
- PluginType get pluginType => DefaultPlugin.blank.type();
+ PluginType get pluginType => PluginType.blank;
}
class BlankPluginConfig implements PluginConfig {
diff --git a/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart b/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart
index e69de29bb2..ac72b6e24e 100644
--- a/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart
+++ b/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart
@@ -0,0 +1,290 @@
+import 'dart:async';
+import 'package:app_flowy/plugins/grid/application/block/block_cache.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_cache.dart';
+import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
+import 'package:app_flowy/plugins/grid/application/row/row_service.dart';
+import 'package:appflowy_board/appflowy_board.dart';
+import 'package:dartz/dartz.dart';
+import 'package:equatable/equatable.dart';
+import 'package:flowy_sdk/log.dart';
+import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'dart:collection';
+
+import 'board_data_controller.dart';
+import 'group_controller.dart';
+
+part 'board_bloc.freezed.dart';
+
+class BoardBloc extends Bloc {
+ final BoardDataController _dataController;
+ late final AFBoardDataController afBoardDataController;
+ final MoveRowFFIService _rowService;
+ Map groupControllers = {};
+
+ GridFieldCache get fieldCache => _dataController.fieldCache;
+ String get gridId => _dataController.gridId;
+
+ BoardBloc({required ViewPB view})
+ : _rowService = MoveRowFFIService(gridId: view.id),
+ _dataController = BoardDataController(view: view),
+ super(BoardState.initial(view.id)) {
+ afBoardDataController = AFBoardDataController(
+ onMoveColumn: (
+ fromIndex,
+ toIndex,
+ ) {},
+ onMoveColumnItem: (
+ columnId,
+ fromIndex,
+ toIndex,
+ ) {
+ final fromRow = groupControllers[columnId]?.rowAtIndex(fromIndex);
+ final toRow = groupControllers[columnId]?.rowAtIndex(toIndex);
+ _moveRow(fromRow, toRow);
+ },
+ onMoveColumnItemToColumn: (
+ fromColumnId,
+ fromIndex,
+ toColumnId,
+ toIndex,
+ ) {
+ final fromRow = groupControllers[fromColumnId]?.rowAtIndex(fromIndex);
+ final toRow = groupControllers[toColumnId]?.rowAtIndex(toIndex);
+ _moveRow(fromRow, toRow);
+ },
+ );
+
+ on(
+ (event, emit) async {
+ await event.when(
+ initial: () async {
+ _startListening();
+ await _loadGrid(emit);
+ },
+ createRow: (groupId) async {
+ final result = await _dataController.createBoardCard(groupId);
+ result.fold(
+ (rowPB) {
+ emit(state.copyWith(editingRow: some(rowPB)));
+ },
+ (err) => Log.error(err),
+ );
+ },
+ endEditRow: (rowId) {
+ assert(state.editingRow.isSome());
+ state.editingRow.fold(() => null, (row) {
+ assert(row.id == rowId);
+ emit(state.copyWith(editingRow: none()));
+ });
+ },
+ didReceiveGridUpdate: (GridPB grid) {
+ emit(state.copyWith(grid: Some(grid)));
+ },
+ didReceiveRows: (List rowInfos) {
+ emit(state.copyWith(rowInfos: rowInfos));
+ },
+ didReceiveError: (FlowyError error) {
+ emit(state.copyWith(noneOrError: some(error)));
+ },
+ );
+ },
+ );
+ }
+
+ void _moveRow(RowPB? fromRow, RowPB? toRow) {
+ if (fromRow != null && toRow != null) {
+ _rowService
+ .moveRow(
+ fromRowId: fromRow.id,
+ toRowId: toRow.id,
+ )
+ .then((result) {
+ result.fold((l) => null, (r) => add(BoardEvent.didReceiveError(r)));
+ });
+ }
+ }
+
+ @override
+ Future close() async {
+ await _dataController.dispose();
+ for (final controller in groupControllers.values) {
+ controller.dispose();
+ }
+ return super.close();
+ }
+
+ void initializeGroups(List groups) {
+ for (final group in groups) {
+ final delegate = GroupControllerDelegateImpl(afBoardDataController);
+ final controller = GroupController(
+ gridId: state.gridId,
+ group: group,
+ delegate: delegate,
+ );
+ controller.startListening();
+ groupControllers[controller.group.groupId] = (controller);
+ }
+ }
+
+ GridRowCache? getRowCache(String blockId) {
+ final GridBlockCache? blockCache = _dataController.blocks[blockId];
+ return blockCache?.rowCache;
+ }
+
+ void _startListening() {
+ _dataController.addListener(
+ onGridChanged: (grid) {
+ if (!isClosed) {
+ add(BoardEvent.didReceiveGridUpdate(grid));
+ }
+ },
+ didLoadGroups: (groups) {
+ List columns = groups.map((group) {
+ return AFBoardColumnData(
+ id: group.groupId,
+ desc: group.desc,
+ items: _buildRows(group.rows),
+ customData: group,
+ );
+ }).toList();
+
+ afBoardDataController.addColumns(columns);
+ initializeGroups(groups);
+ },
+ onRowsChanged: (List rowInfos, RowsChangedReason reason) {
+ add(BoardEvent.didReceiveRows(rowInfos));
+ },
+ onError: (err) {
+ Log.error(err);
+ },
+ );
+ }
+
+ List _buildRows(List rows) {
+ final items = rows.map((row) {
+ return BoardColumnItem(row: row);
+ }).toList();
+
+ return [...items];
+ }
+
+ Future _loadGrid(Emitter emit) async {
+ final result = await _dataController.loadData();
+ result.fold(
+ (grid) => emit(
+ state.copyWith(loadingState: GridLoadingState.finish(left(unit))),
+ ),
+ (err) => emit(
+ state.copyWith(loadingState: GridLoadingState.finish(right(err))),
+ ),
+ );
+ }
+}
+
+@freezed
+class BoardEvent with _$BoardEvent {
+ const factory BoardEvent.initial() = InitialGrid;
+ const factory BoardEvent.createRow(String groupId) = _CreateRow;
+ const factory BoardEvent.endEditRow(String rowId) = _EndEditRow;
+ const factory BoardEvent.didReceiveError(FlowyError error) = _DidReceiveError;
+ const factory BoardEvent.didReceiveRows(List rowInfos) =
+ _DidReceiveRows;
+ const factory BoardEvent.didReceiveGridUpdate(
+ GridPB grid,
+ ) = _DidReceiveGridUpdate;
+}
+
+@freezed
+class BoardState with _$BoardState {
+ const factory BoardState({
+ required String gridId,
+ required Option grid,
+ required Option editingRow,
+ required List rowInfos,
+ required GridLoadingState loadingState,
+ required Option noneOrError,
+ }) = _BoardState;
+
+ factory BoardState.initial(String gridId) => BoardState(
+ rowInfos: [],
+ grid: none(),
+ gridId: gridId,
+ editingRow: none(),
+ noneOrError: none(),
+ loadingState: const _Loading(),
+ );
+}
+
+@freezed
+class GridLoadingState with _$GridLoadingState {
+ const factory GridLoadingState.loading() = _Loading;
+ const factory GridLoadingState.finish(
+ Either successOrFail) = _Finish;
+}
+
+class GridFieldEquatable extends Equatable {
+ final UnmodifiableListView _fields;
+ const GridFieldEquatable(
+ UnmodifiableListView fields,
+ ) : _fields = fields;
+
+ @override
+ List