mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge branch 'main' into feat/history
This commit is contained in:
commit
1e3c69f15b
26
.githooks/commit-msg
Executable file
26
.githooks/commit-msg
Executable 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
4
.githooks/pre-commit
Normal file → Executable 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
5
.githooks/pre-push
Normal file → Executable 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'
|
||||||
|
|
||||||
|
4
.github/workflows/ci.yaml
vendored
4
.github/workflows/ci.yaml
vendored
@ -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
|
||||||
|
19
.github/workflows/dart_lint.yml
vendored
19
.github/workflows/dart_lint.yml
vendored
@ -25,14 +25,31 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
flutter-version: '3.0.0'
|
flutter-version: '3.0.0'
|
||||||
channel: "stable"
|
channel: "stable"
|
||||||
- name: Deps Flutter
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: 'stable-2022-01-20'
|
||||||
|
|
||||||
|
- name: Rust Deps
|
||||||
|
working-directory: frontend
|
||||||
|
run: |
|
||||||
|
cargo install cargo-make
|
||||||
|
cargo make flowy_dev
|
||||||
|
|
||||||
|
- name: Flutter Deps
|
||||||
run: flutter packages pub get
|
run: flutter packages pub get
|
||||||
working-directory: frontend/app_flowy
|
working-directory: frontend/app_flowy
|
||||||
|
|
||||||
|
- name: Build FlowySDK
|
||||||
|
working-directory: frontend
|
||||||
|
run: |
|
||||||
|
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
|
||||||
run: |
|
run: |
|
||||||
flutter packages pub run easy_localization:generate -f keys -o locale_keys.g.dart -S assets/translations -s en.json
|
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
|
flutter packages pub run build_runner build --delete-conflicting-outputs
|
||||||
|
|
||||||
- name: Run Flutter Analyzer
|
- name: Run Flutter Analyzer
|
||||||
working-directory: frontend/app_flowy
|
working-directory: frontend/app_flowy
|
||||||
run: flutter analyze
|
run: flutter analyze
|
||||||
|
16
.github/workflows/dart_test.yml
vendored
16
.github/workflows/dart_test.yml
vendored
@ -43,18 +43,21 @@ jobs:
|
|||||||
shared-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: Deps Flutter
|
- name: Flutter Deps
|
||||||
working-directory: frontend/app_flowy
|
working-directory: frontend/app_flowy
|
||||||
run: |
|
run: |
|
||||||
flutter config --enable-linux-desktop
|
flutter config --enable-linux-desktop
|
||||||
|
|
||||||
- name: Deps Rust
|
- name: Rust Deps
|
||||||
working-directory: frontend
|
working-directory: frontend
|
||||||
run: |
|
run: |
|
||||||
cargo install cargo-make
|
cargo install cargo-make
|
||||||
cargo install duckscript_cli
|
|
||||||
cargo make flowy_dev
|
cargo make flowy_dev
|
||||||
|
- name: Build FlowySDK
|
||||||
|
working-directory: frontend
|
||||||
|
run: |
|
||||||
|
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
|
||||||
run: |
|
run: |
|
||||||
@ -62,11 +65,6 @@ jobs:
|
|||||||
flutter packages pub run easy_localization:generate -f keys -o locale_keys.g.dart -S assets/translations -s en.json
|
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
|
flutter packages pub run build_runner build --delete-conflicting-outputs
|
||||||
|
|
||||||
- name: Build FlowySDK
|
|
||||||
working-directory: frontend
|
|
||||||
run: |
|
|
||||||
cargo make --profile development-linux-x86 flowy-sdk-dev
|
|
||||||
|
|
||||||
- name: Run bloc tests
|
- name: Run bloc tests
|
||||||
working-directory: frontend/app_flowy
|
working-directory: frontend/app_flowy
|
||||||
run: |
|
run: |
|
||||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -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
|
||||||
|
36
.github/workflows/rust_lint.yml
vendored
36
.github/workflows/rust_lint.yml
vendored
@ -17,25 +17,31 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: 'stable-2022-01-20'
|
toolchain: 'stable-2022-01-20'
|
||||||
override: true
|
override: true
|
||||||
|
|
||||||
|
- name: Rust Deps
|
||||||
|
working-directory: frontend
|
||||||
|
run: |
|
||||||
|
cargo install cargo-make
|
||||||
|
cargo make flowy_dev
|
||||||
|
|
||||||
|
- name: Build FlowySDK
|
||||||
|
working-directory: frontend
|
||||||
|
run: |
|
||||||
|
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
|
||||||
- run: cargo fmt --all -- --check
|
- name: rustfmt
|
||||||
|
run: cargo fmt --all -- --check
|
||||||
working-directory: frontend/rust-lib/
|
working-directory: frontend/rust-lib/
|
||||||
|
|
||||||
|
|
||||||
rust-clippy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Clippy
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: 'stable-2022-01-20'
|
|
||||||
override: true
|
|
||||||
- run: rustup component add clippy
|
- run: rustup component add clippy
|
||||||
|
working-directory: frontend/rust-lib
|
||||||
|
- name: clippy
|
||||||
|
run: cargo clippy --no-default-features
|
||||||
working-directory: frontend/rust-lib
|
working-directory: frontend/rust-lib
|
||||||
- run: cargo clippy --no-default-features
|
|
||||||
working-directory: frontend/rust-lib
|
|
||||||
|
16
.gitignore
vendored
16
.gitignore
vendored
@ -17,4 +17,18 @@ yarn.lock
|
|||||||
node_modules
|
node_modules
|
||||||
**/.proto_cache
|
**/.proto_cache
|
||||||
**/.cache
|
**/.cache
|
||||||
**/.DS_Store
|
**/.DS_Store
|
||||||
|
|
||||||
|
**/src/protobuf
|
||||||
|
**/resources/proto
|
||||||
|
|
||||||
|
frontend/.vscode/*
|
||||||
|
!frontend/.vscode/settings.json
|
||||||
|
!frontend/.vscode/tasks.json
|
||||||
|
!frontend/.vscode/launch.json
|
||||||
|
!frontend/.vscode/extensions.json
|
||||||
|
!frontend/.vscode/*.code-snippets
|
||||||
|
|
||||||
|
# Commit the highest level pubspec.lock, but ignore the others
|
||||||
|
pubspec.lock
|
||||||
|
!frontend/app_flowy/pubspec.lock
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
. "$(dirname "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
npx --no -- commitlint --edit
|
|
@ -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"
|
|
@ -23,7 +23,8 @@ You are in charge of your data and customizations.
|
|||||||
<a href="https://twitter.com/appflowy"><b>Twitter</b></a>
|
<a href="https://twitter.com/appflowy"><b>Twitter</b></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center"><img src="https://github.com/AppFlowy-IO/appflowy/blob/main/doc/imgs/welcome.png" alt="The Open Source Alternative To Notion." width="1000px" /></p>
|
<p align="center"><img src="https://user-images.githubusercontent.com/12026239/174754661-682980e4-e386-4685-bb6f-2da357390b61.png" alt="The Open Source Alternative To Notion." width="1000px" /></p>
|
||||||
|
<p align="center"><img src="https://user-images.githubusercontent.com/12026239/174753177-98e4c899-2356-4137-bb42-374bba2b127b.png" alt="The Open Source Alternative To Notion." width="1000px" /></p>
|
||||||
|
|
||||||
## User Installation
|
## User Installation
|
||||||
|
|
||||||
|
61
frontend/.vscode/launch.json
vendored
61
frontend/.vscode/launch.json
vendored
@ -5,40 +5,67 @@
|
|||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "app_flowy",
|
// This task builds the Rust and Dart code of AppFlowy.
|
||||||
|
"name": "AF: Build All",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "./lib/main.dart",
|
"program": "./lib/main.dart",
|
||||||
"type": "dart",
|
"type": "dart",
|
||||||
"preLaunchTask": "build_flowy_sdk",
|
"preLaunchTask": "AF: build_flowy_sdk",
|
||||||
"env":{
|
"env": {
|
||||||
"RUST_LOG":"info"
|
"RUST_LOG": "info"
|
||||||
},
|
},
|
||||||
"cwd": "${workspaceRoot}/app_flowy"
|
"cwd": "${workspaceRoot}/app_flowy"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "app_flowy(trace)",
|
"name": "AF: Debug Rust",
|
||||||
|
"request": "attach",
|
||||||
|
"type": "lldb",
|
||||||
|
"pid": "${command:pickMyProcess}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// This task only builds the Dart code of AppFlowy.
|
||||||
|
"name": "AF: Build Dart Only",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "./lib/main.dart",
|
"program": "./lib/main.dart",
|
||||||
"type": "dart",
|
"type": "dart",
|
||||||
"preLaunchTask": "build_flowy_sdk",
|
"env": {
|
||||||
"env":{
|
"RUST_LOG": "debug"
|
||||||
"RUST_LOG":"trace"
|
|
||||||
},
|
},
|
||||||
"cwd": "${workspaceRoot}/app_flowy"
|
"cwd": "${workspaceRoot}/app_flowy"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "app_flowy (profile mode)",
|
// This task builds will:
|
||||||
"request": "launch",
|
// - call the clean task,
|
||||||
"type": "dart",
|
// - rebuild all the generated Files (including freeze and language files)
|
||||||
"flutterMode": "profile"
|
// - rebuild the the Rust and Dart code of AppFlowy.
|
||||||
},
|
"name": "AF: Clean + Rebuild All",
|
||||||
{
|
|
||||||
"name": "Generate Language Files",
|
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "./lib/main.dart",
|
"program": "./lib/main.dart",
|
||||||
"type": "dart",
|
"type": "dart",
|
||||||
"preLaunchTask": "Generate Language Files",
|
"preLaunchTask": "AF: Clean + Rebuild All",
|
||||||
"cwd": "${workspaceRoot}/app_flowy/"
|
"env": {
|
||||||
|
"RUST_LOG": "info"
|
||||||
|
},
|
||||||
|
"cwd": "${workspaceRoot}/app_flowy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AF: Build All (rustlog: trace)",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "./lib/main.dart",
|
||||||
|
"type": "dart",
|
||||||
|
"preLaunchTask": "AF: build_flowy_sdk",
|
||||||
|
"env": {
|
||||||
|
"RUST_LOG": "trace"
|
||||||
|
},
|
||||||
|
"cwd": "${workspaceRoot}/app_flowy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AF: app_flowy (profile mode)",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "./lib/main.dart",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "profile",
|
||||||
|
"cwd": "${workspaceRoot}/app_flowy"
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
1
frontend/.vscode/settings.json
vendored
1
frontend/.vscode/settings.json
vendored
@ -23,4 +23,5 @@
|
|||||||
"*.log.*": "log"
|
"*.log.*": "log"
|
||||||
},
|
},
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
|
"files.eol": "\n",
|
||||||
}
|
}
|
121
frontend/.vscode/tasks.json
vendored
121
frontend/.vscode/tasks.json
vendored
@ -10,13 +10,31 @@
|
|||||||
// ${cwd}: the current working directory of the spawned process
|
// ${cwd}: the current working directory of the spawned process
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"label": "build_flowy_sdk",
|
"label": "AF: Clean + Rebuild All",
|
||||||
|
"type": "shell",
|
||||||
|
"dependsOrder": "sequence",
|
||||||
|
"dependsOn": [
|
||||||
|
"AF: Rust Clean",
|
||||||
|
"AF: Flutter Clean",
|
||||||
|
"AF: build_flowy_sdk",
|
||||||
|
"AF: Flutter Pub Get",
|
||||||
|
"AF: Flutter Package Get",
|
||||||
|
"AF: Generate Language Files",
|
||||||
|
"AF: Generate Freezed Files",
|
||||||
|
],
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "always",
|
||||||
|
"panel": "new"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "AF: build_flowy_sdk",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "sh ./scripts/build_sdk.sh",
|
"command": "sh ./scripts/build_sdk.sh",
|
||||||
"windows": {
|
"windows": {
|
||||||
"options": {
|
"options": {
|
||||||
"env": {
|
"env": {
|
||||||
"FLOWY_DEV_ENV": "Windows",
|
"FLOWY_DEV_ENV": "Windows"
|
||||||
},
|
},
|
||||||
"shell": {
|
"shell": {
|
||||||
"executable": "cmd.exe",
|
"executable": "cmd.exe",
|
||||||
@ -31,27 +49,76 @@
|
|||||||
"linux": {
|
"linux": {
|
||||||
"options": {
|
"options": {
|
||||||
"env": {
|
"env": {
|
||||||
"FLOWY_DEV_ENV": "Linux-x86",
|
"FLOWY_DEV_ENV": "Linux"
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
"osx": {
|
"osx": {
|
||||||
"options": {
|
"options": {
|
||||||
"env": {
|
"env": {
|
||||||
"FLOWY_DEV_ENV": "macOS",
|
"FLOWY_DEV_ENV": "macOS"
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
"group": "build",
|
"group": "build",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}"
|
||||||
},
|
}
|
||||||
// "problemMatcher": [
|
|
||||||
// "$rustc"
|
|
||||||
// ],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Generate Language Files",
|
"label": "AF: Code Gen",
|
||||||
|
"type": "shell",
|
||||||
|
"dependsOrder": "sequence",
|
||||||
|
"dependsOn": [
|
||||||
|
"AF: Flutter Clean",
|
||||||
|
"AF: Flutter Pub Get",
|
||||||
|
"AF: Flutter Package Get",
|
||||||
|
"AF: Generate Language Files",
|
||||||
|
"AF: Generate Freezed Files"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "always",
|
||||||
|
"panel": "new"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "AF: Flutter Clean",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "flutter clean",
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}/app_flowy"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "AF: Flutter Pub Get",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "flutter pub get",
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}/app_flowy"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "AF: Flutter Package Get",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "flutter packages pub get",
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}/app_flowy"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "AF: Generate Freezed Files",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "flutter pub run build_runner build --delete-conflicting-outputs",
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}/app_flowy"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "AF: Generate Language Files",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "sh ./scripts/generate_language_files.sh",
|
"command": "sh ./scripts/generate_language_files.sh",
|
||||||
"windows": {
|
"windows": {
|
||||||
@ -69,28 +136,28 @@
|
|||||||
"group": "build",
|
"group": "build",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}"
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Clean",
|
"label": "AF: Rust Clean",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "sh ./scripts/clean.sh",
|
"command": "cargo make flowy_clean",
|
||||||
"windows": {
|
|
||||||
"options": {
|
|
||||||
"shell": {
|
|
||||||
"executable": "cmd.exe",
|
|
||||||
"args": [
|
|
||||||
"/d",
|
|
||||||
"/c",
|
|
||||||
".\\scripts\\clean.cmd"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"group": "build",
|
"group": "build",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}"
|
||||||
},
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "AF: flutter build aar",
|
||||||
|
"type": "flutter",
|
||||||
|
"command": "flutter",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"aar"
|
||||||
|
],
|
||||||
|
"group": "build",
|
||||||
|
"problemMatcher": [],
|
||||||
|
"detail": "app_flowy"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -1,2 +0,0 @@
|
|||||||
brew 'sqlite3'
|
|
||||||
brew 'rustup-init'
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
@ -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]
|
||||||
@ -52,6 +53,7 @@ RUST_COMPILE_TARGET = "aarch64-apple-darwin"
|
|||||||
BUILD_FLAG = "debug"
|
BUILD_FLAG = "debug"
|
||||||
FLUTTER_OUTPUT_DIR = "Debug"
|
FLUTTER_OUTPUT_DIR = "Debug"
|
||||||
PRODUCT_EXT = "app"
|
PRODUCT_EXT = "app"
|
||||||
|
BUILD_ARCHS = "arm64"
|
||||||
|
|
||||||
[env.development-mac-x86_64]
|
[env.development-mac-x86_64]
|
||||||
RUST_LOG = "info"
|
RUST_LOG = "info"
|
||||||
@ -60,6 +62,7 @@ RUST_COMPILE_TARGET = "x86_64-apple-darwin"
|
|||||||
BUILD_FLAG = "debug"
|
BUILD_FLAG = "debug"
|
||||||
FLUTTER_OUTPUT_DIR = "Debug"
|
FLUTTER_OUTPUT_DIR = "Debug"
|
||||||
PRODUCT_EXT = "app"
|
PRODUCT_EXT = "app"
|
||||||
|
BUILD_ARCHS = "x86_64"
|
||||||
|
|
||||||
[env.production-mac-arm64]
|
[env.production-mac-arm64]
|
||||||
BUILD_FLAG = "release"
|
BUILD_FLAG = "release"
|
||||||
@ -68,6 +71,7 @@ RUST_COMPILE_TARGET = "aarch64-apple-darwin"
|
|||||||
FLUTTER_OUTPUT_DIR = "Release"
|
FLUTTER_OUTPUT_DIR = "Release"
|
||||||
PRODUCT_EXT = "app"
|
PRODUCT_EXT = "app"
|
||||||
APP_ENVIRONMENT = "production"
|
APP_ENVIRONMENT = "production"
|
||||||
|
BUILD_ARCHS = "arm64"
|
||||||
|
|
||||||
[env.production-mac-x86_64]
|
[env.production-mac-x86_64]
|
||||||
BUILD_FLAG = "release"
|
BUILD_FLAG = "release"
|
||||||
@ -76,6 +80,7 @@ RUST_COMPILE_TARGET = "x86_64-apple-darwin"
|
|||||||
FLUTTER_OUTPUT_DIR = "Release"
|
FLUTTER_OUTPUT_DIR = "Release"
|
||||||
PRODUCT_EXT = "app"
|
PRODUCT_EXT = "app"
|
||||||
APP_ENVIRONMENT = "production"
|
APP_ENVIRONMENT = "production"
|
||||||
|
BUILD_ARCHS = "x86_64"
|
||||||
|
|
||||||
[env.development-windows-x86]
|
[env.development-windows-x86]
|
||||||
TARGET_OS = "windows"
|
TARGET_OS = "windows"
|
||||||
@ -96,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"
|
||||||
@ -105,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"
|
||||||
@ -146,6 +151,7 @@ script = [
|
|||||||
echo PRODUCT_EXT: ${PRODUCT_EXT}
|
echo PRODUCT_EXT: ${PRODUCT_EXT}
|
||||||
echo APP_ENVIRONMENT: ${APP_ENVIRONMENT}
|
echo APP_ENVIRONMENT: ${APP_ENVIRONMENT}
|
||||||
echo ${platforms}
|
echo ${platforms}
|
||||||
|
echo ${BUILD_ARCHS}
|
||||||
'''
|
'''
|
||||||
]
|
]
|
||||||
script_runner = "@shell"
|
script_runner = "@shell"
|
||||||
|
48
frontend/app_flowy/.vscode/launch.json
vendored
48
frontend/app_flowy/.vscode/launch.json
vendored
@ -1,48 +0,0 @@
|
|||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
// This task builds the Rust and Dart code of AppFlowy.
|
|
||||||
"name": "Build",
|
|
||||||
"request": "launch",
|
|
||||||
"program": "${workspaceRoot}/lib/main.dart",
|
|
||||||
"preLaunchTask": "build_flowy_sdk",
|
|
||||||
"type": "dart",
|
|
||||||
"env": {
|
|
||||||
"RUST_LOG": "debug"
|
|
||||||
},
|
|
||||||
"cwd": "${workspaceRoot}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// This task only build the Dart code of AppFlowy.
|
|
||||||
"name": "Build (Dart)",
|
|
||||||
"request": "launch",
|
|
||||||
"program": "${workspaceRoot}/lib/main.dart",
|
|
||||||
"type": "dart",
|
|
||||||
"env": {
|
|
||||||
"RUST_LOG": "debug"
|
|
||||||
},
|
|
||||||
"cwd": "${workspaceRoot}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Build (trace log)",
|
|
||||||
"request": "launch",
|
|
||||||
"program": "${workspaceRoot}/lib/main.dart",
|
|
||||||
"type": "dart",
|
|
||||||
"preLaunchTask": "build_flowy_sdk",
|
|
||||||
"env": {
|
|
||||||
"RUST_LOG": "trace"
|
|
||||||
},
|
|
||||||
"cwd": "${workspaceRoot}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Build (profile mode)",
|
|
||||||
"request": "launch",
|
|
||||||
"type": "dart",
|
|
||||||
"flutterMode": "profile"
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
26
frontend/app_flowy/.vscode/settings.json
vendored
26
frontend/app_flowy/.vscode/settings.json
vendored
@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"[dart]": {
|
|
||||||
"editor.formatOnSave": true,
|
|
||||||
"editor.formatOnType": true,
|
|
||||||
"editor.rulers": [
|
|
||||||
120
|
|
||||||
],
|
|
||||||
"editor.selectionHighlight": false,
|
|
||||||
"editor.suggest.snippetsPreventQuickSuggestions": false,
|
|
||||||
"editor.suggestSelection": "first",
|
|
||||||
"editor.tabCompletion": "onlySnippets",
|
|
||||||
"editor.wordBasedSuggestions": false
|
|
||||||
},
|
|
||||||
"svgviewer.enableautopreview": true,
|
|
||||||
"svgviewer.previewcolumn": "Active",
|
|
||||||
"svgviewer.showzoominout": true,
|
|
||||||
"editor.wordWrapColumn": 120,
|
|
||||||
"editor.minimap.maxColumn": 140,
|
|
||||||
"prettier.printWidth": 140,
|
|
||||||
"editor.wordWrap": "wordWrapColumn",
|
|
||||||
"dart.lineLength": 120,
|
|
||||||
"files.associations": {
|
|
||||||
"*.log.*": "log"
|
|
||||||
},
|
|
||||||
"editor.formatOnSave": true,
|
|
||||||
}
|
|
129
frontend/app_flowy/.vscode/tasks.json
vendored
129
frontend/app_flowy/.vscode/tasks.json
vendored
@ -1,129 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "2.0.0",
|
|
||||||
// https://code.visualstudio.com/docs/editor/tasks
|
|
||||||
// https://gist.github.com/deadalusai/9e13e36d61ec7fb72148
|
|
||||||
// ${workspaceRoot}: the root folder of the team
|
|
||||||
// ${file}: the current opened file
|
|
||||||
// ${fileBasename}: the current opened file's basename
|
|
||||||
// ${fileDirname}: the current opened file's dirname
|
|
||||||
// ${fileExtname}: the current opened file's extension
|
|
||||||
// ${cwd}: the current working directory of the spawned process
|
|
||||||
"tasks": [
|
|
||||||
{
|
|
||||||
"label": "build_flowy_sdk",
|
|
||||||
"type": "shell",
|
|
||||||
"command": "sh ./scripts/build_sdk.sh",
|
|
||||||
"windows": {
|
|
||||||
"options": {
|
|
||||||
"env": {
|
|
||||||
"FLOWY_DEV_ENV": "Windows",
|
|
||||||
},
|
|
||||||
"shell": {
|
|
||||||
"executable": "cmd.exe",
|
|
||||||
"args": [
|
|
||||||
"/d",
|
|
||||||
"/c",
|
|
||||||
".\\scripts\\build_sdk.cmd"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"linux": {
|
|
||||||
"options": {
|
|
||||||
"env": {
|
|
||||||
"FLOWY_DEV_ENV": "Linux-x86",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"osx": {
|
|
||||||
"options": {
|
|
||||||
"env": {
|
|
||||||
"FLOWY_DEV_ENV": "macOS",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"group": "build",
|
|
||||||
"options": {
|
|
||||||
"cwd": "${workspaceFolder}/../"
|
|
||||||
},
|
|
||||||
// "problemMatcher": [
|
|
||||||
// "$rustc"
|
|
||||||
// ],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Code Gen",
|
|
||||||
"type": "shell",
|
|
||||||
"dependsOn": [
|
|
||||||
"Flutter Pub",
|
|
||||||
"Flutter Package Get",
|
|
||||||
"Generate Language Files",
|
|
||||||
"Generate Freezed Files"
|
|
||||||
],
|
|
||||||
"group": {
|
|
||||||
"kind": "build",
|
|
||||||
"isDefault": true,
|
|
||||||
},
|
|
||||||
"dependsOrder": "sequence",
|
|
||||||
"presentation": {
|
|
||||||
"reveal": "always",
|
|
||||||
"panel": "new"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Flutter Pub",
|
|
||||||
"type": "shell",
|
|
||||||
"command": "flutter pub get",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Flutter Package Get",
|
|
||||||
"type": "shell",
|
|
||||||
"command": "flutter packages pub get",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Generate Freezed Files",
|
|
||||||
"type": "shell",
|
|
||||||
"command": "flutter pub run build_runner build --delete-conflicting-outputs",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Generate Language Files",
|
|
||||||
"type": "shell",
|
|
||||||
"command": "sh ./scripts/generate_language_files.sh",
|
|
||||||
"windows": {
|
|
||||||
"options": {
|
|
||||||
"shell": {
|
|
||||||
"executable": "cmd.exe",
|
|
||||||
"args": [
|
|
||||||
"/d",
|
|
||||||
"/c",
|
|
||||||
".\\scripts\\generate_language_files.cmd"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"group": "build",
|
|
||||||
"options": {
|
|
||||||
"cwd": "${workspaceFolder}/../"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Clean",
|
|
||||||
"type": "shell",
|
|
||||||
"command": "sh ./scripts/clean.sh",
|
|
||||||
"windows": {
|
|
||||||
"options": {
|
|
||||||
"shell": {
|
|
||||||
"executable": "cmd.exe",
|
|
||||||
"args": [
|
|
||||||
"/d",
|
|
||||||
"/c",
|
|
||||||
".\\scripts\\clean.cmd"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"options": {
|
|
||||||
"cwd": "${workspaceFolder}/../"
|
|
||||||
},
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -17,7 +17,6 @@ analyzer:
|
|||||||
- "packages/flowy_editor/**"
|
- "packages/flowy_editor/**"
|
||||||
- "packages/editor/**"
|
- "packages/editor/**"
|
||||||
# - "packages/flowy_infra_ui/**"
|
# - "packages/flowy_infra_ui/**"
|
||||||
|
|
||||||
linter:
|
linter:
|
||||||
# The lint rules applied to this project can be customized in the
|
# The lint rules applied to this project can be customized in the
|
||||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||||
@ -38,4 +37,4 @@ linter:
|
|||||||
# https://dart.dev/guides/language/analysis-options
|
# https://dart.dev/guides/language/analysis-options
|
||||||
|
|
||||||
errors:
|
errors:
|
||||||
invalid_annotation_target: ignore
|
invalid_annotation_target: ignore
|
||||||
|
@ -96,6 +96,12 @@
|
|||||||
"lightMode": "Switch to Light mode",
|
"lightMode": "Switch to Light mode",
|
||||||
"darkMode": "Switch to Dark mode"
|
"darkMode": "Switch to Dark mode"
|
||||||
},
|
},
|
||||||
|
"notifications": {
|
||||||
|
"export": {
|
||||||
|
"markdown": "Exported Note To Markdown",
|
||||||
|
"path": "Documents/flowy"
|
||||||
|
}
|
||||||
|
},
|
||||||
"contactsPage": {
|
"contactsPage": {
|
||||||
"title": "Contacts",
|
"title": "Contacts",
|
||||||
"whatsHappening": "What's happening this week?",
|
"whatsHappening": "What's happening this week?",
|
||||||
@ -199,6 +205,10 @@
|
|||||||
"pannelTitle": "Select an option or create one",
|
"pannelTitle": "Select an option or create one",
|
||||||
"searchOption": "Search for an option"
|
"searchOption": "Search for an option"
|
||||||
},
|
},
|
||||||
|
"menuName": "Grid"
|
||||||
|
},
|
||||||
|
"document": {
|
||||||
|
"menuName": "Doc",
|
||||||
"date": {
|
"date": {
|
||||||
"timeHintTextInTwelveHour": "12:00 AM",
|
"timeHintTextInTwelveHour": "12:00 AM",
|
||||||
"timeHintTextInTwentyFourHour": "12:00"
|
"timeHintTextInTwentyFourHour": "12:00"
|
||||||
|
@ -141,5 +141,11 @@
|
|||||||
"lightLabel": "Modalità Chiara",
|
"lightLabel": "Modalità Chiara",
|
||||||
"darkLabel": "Modalità Scura"
|
"darkLabel": "Modalità Scura"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"grid": {
|
||||||
|
"menuName":"Griglia"
|
||||||
|
},
|
||||||
|
"document":{
|
||||||
|
"menuName":"Documento"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
199
frontend/app_flowy/assets/translations/ja-JP.json
Normal file
199
frontend/app_flowy/assets/translations/ja-JP.json
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
{
|
||||||
|
"appName": "AppFlowy",
|
||||||
|
"defaultUsername": "ユーザー",
|
||||||
|
"welcomeText": "Welcome to @:appName",
|
||||||
|
"githubStarText": "Star on GitHub",
|
||||||
|
"subscribeNewsletterText": "新着情報を受け取る",
|
||||||
|
"letsGoButtonText": "Let's Go",
|
||||||
|
"title": "タイトル",
|
||||||
|
"signUp": {
|
||||||
|
"buttonText": "新規登録",
|
||||||
|
"title": "@:appNameに新規登録",
|
||||||
|
"getStartedText": "はじめる",
|
||||||
|
"emptyPasswordError": "パスワードを空にはできません",
|
||||||
|
"repeatPasswordEmptyError": "パスワード(確認用)を空にはできません",
|
||||||
|
"unmatchedPasswordError": "パスワード(確認用)が一致しません",
|
||||||
|
"alreadyHaveAnAccount": "すでにアカウントを登録済ですか?",
|
||||||
|
"emailHint": "メールアドレス",
|
||||||
|
"passwordHint": "パスワード",
|
||||||
|
"repeatPasswordHint": "パスワード(確認用)"
|
||||||
|
},
|
||||||
|
"signIn": {
|
||||||
|
"loginTitle": "@:appName にログイン",
|
||||||
|
"loginButtonText": "ログイン",
|
||||||
|
"buttonText": "サインイン",
|
||||||
|
"forgotPassword": "パスワードをお忘れですか?",
|
||||||
|
"emailHint": "メールアドレス",
|
||||||
|
"passwordHint": "パスワード",
|
||||||
|
"dontHaveAnAccount": "まだアカウントをお持ちではないですか?",
|
||||||
|
"repeatPasswordEmptyError": "パスワード(確認用)を空にはできません",
|
||||||
|
"unmatchedPasswordError": "パスワード(確認用)が一致しません"
|
||||||
|
},
|
||||||
|
"workspace": {
|
||||||
|
"create": "ワークスペースを作成する",
|
||||||
|
"hint": "ワークスペース",
|
||||||
|
"notFoundError": "ワークスペースがみつかりません"
|
||||||
|
},
|
||||||
|
"shareAction": {
|
||||||
|
"buttonText": "共有する",
|
||||||
|
"workInProgress": "Coming soon",
|
||||||
|
"markdown": "Markdown",
|
||||||
|
"copyLink": "Copy Link"
|
||||||
|
},
|
||||||
|
"disclosureAction": {
|
||||||
|
"rename": "名前を変更",
|
||||||
|
"delete": "削除",
|
||||||
|
"duplicate": "コピーを作成"
|
||||||
|
},
|
||||||
|
"blankPageTitle": "空のページ",
|
||||||
|
"newPageText": "新しいページ",
|
||||||
|
"trash": {
|
||||||
|
"text": "ごみ箱",
|
||||||
|
"restoreAll": "全て復元",
|
||||||
|
"deleteAll": "全て削除",
|
||||||
|
"pageHeader": {
|
||||||
|
"fileName": "ファイル名",
|
||||||
|
"lastModified": "最終更新日時",
|
||||||
|
"created": "作成日時"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deletePagePrompt": {
|
||||||
|
"text": "このページはごみ箱にあります",
|
||||||
|
"restore": "ページを元に戻す",
|
||||||
|
"deletePermanent": "削除する"
|
||||||
|
},
|
||||||
|
"dialogCreatePageNameHint": "ページ名",
|
||||||
|
"questionBubble": {
|
||||||
|
"whatsNew": "What's new?",
|
||||||
|
"help": "ヘルプとサポート",
|
||||||
|
"debug": {
|
||||||
|
"name": "デバッグ情報",
|
||||||
|
"success": "デバッグ情報をクリップボードにコピーしました!",
|
||||||
|
"fail": "デバッグ情報をクリップボードにコピーできませんでした"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"menuAppHeader": {
|
||||||
|
"addPageTooltip": "内部ページを追加",
|
||||||
|
"defaultNewPageName": "Untitled",
|
||||||
|
"renameDialog": "名前を変更"
|
||||||
|
},
|
||||||
|
"toolbar": {
|
||||||
|
"undo": "元に戻す",
|
||||||
|
"redo": "やり直し",
|
||||||
|
"bold": "太字",
|
||||||
|
"italic": "斜体",
|
||||||
|
"underline": "下線",
|
||||||
|
"strike": "取り消し線",
|
||||||
|
"numList": "番号付きリスト",
|
||||||
|
"bulletList": "箇条書き",
|
||||||
|
"checkList": "チェックボックス",
|
||||||
|
"inlineCode": "インラインコード",
|
||||||
|
"quote": "引用文",
|
||||||
|
"header": "見出し",
|
||||||
|
"highlight": "文字の背景色"
|
||||||
|
},
|
||||||
|
"tooltip": {
|
||||||
|
"lightMode": "ライトモードに切り替える",
|
||||||
|
"darkMode": "ダークモードに切り替える"
|
||||||
|
},
|
||||||
|
"contactsPage": {
|
||||||
|
"title": "連絡先",
|
||||||
|
"whatsHappening": "今週はどんなことがありましたか?",
|
||||||
|
"addContact": "連絡先を追加する",
|
||||||
|
"editContact": "連絡先を編集する"
|
||||||
|
},
|
||||||
|
"button": {
|
||||||
|
"OK": "OK",
|
||||||
|
"Cancel": "キャンセル",
|
||||||
|
"signIn": "サインイン",
|
||||||
|
"signOut": "サインアウト",
|
||||||
|
"complete": "完了",
|
||||||
|
"save": "保存"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"welcome": "ようこそ!",
|
||||||
|
"firstName": "名",
|
||||||
|
"middleName": "ミドルネーム",
|
||||||
|
"lastName": "姓",
|
||||||
|
"stepX": "Step {X}"
|
||||||
|
},
|
||||||
|
"oAuth": {
|
||||||
|
"err": {
|
||||||
|
"failedTitle": "アカウントに接続できません",
|
||||||
|
"failedMsg": "サインインが完了したことをブラウザーで確認してください"
|
||||||
|
},
|
||||||
|
"google": {
|
||||||
|
"title": "GOOGLEでサインイン",
|
||||||
|
"instruction1": "GOOGLEでのサインインを有効にするためには、Webブラウザーを使ってこのアプリケーションを認証する必要があります。",
|
||||||
|
"instruction2": "アイコンをクリックするか、以下のテキストを選択して、このコードをクリップボードにコピーします。",
|
||||||
|
"instruction3": "以下のリンク先をブラウザーで開いて、次のコードを入力します。",
|
||||||
|
"instruction4": "登録が完了したら以下のボタンを押してください。"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"title": "設定",
|
||||||
|
"menu": {
|
||||||
|
"appearance": "外観",
|
||||||
|
"language": "言語",
|
||||||
|
"open": "設定"
|
||||||
|
},
|
||||||
|
"appearance": {
|
||||||
|
"lightLabel": "ライトモード",
|
||||||
|
"darkLabel": "ダークモード"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"grid": {
|
||||||
|
"settings": {
|
||||||
|
"filter": "絞り込み",
|
||||||
|
"sortBy": "並び替え",
|
||||||
|
"Properties": "プロパティ"
|
||||||
|
},
|
||||||
|
"field": {
|
||||||
|
"hide": "隠す",
|
||||||
|
"insertLeft": "左に挿入",
|
||||||
|
"insertRight": "右に挿入",
|
||||||
|
"duplicate": "コピーを作成",
|
||||||
|
"delete": "削除",
|
||||||
|
"textFieldName": "テキスト",
|
||||||
|
"checkboxFieldName": "チェックボックス",
|
||||||
|
"dateFieldName": "日付",
|
||||||
|
"numberFieldName": "数値",
|
||||||
|
"singleSelectFieldName": "単一選択",
|
||||||
|
"multiSelectFieldName": "複数選択",
|
||||||
|
"numberFormat": " 数値書式",
|
||||||
|
"dateFormat": " 日付書式",
|
||||||
|
"includeTime": " 時刻を含める",
|
||||||
|
"dateFormatFriendly": "月 日,年",
|
||||||
|
"dateFormatISO": "年-月-日",
|
||||||
|
"dateFormatLocal": "年/月/日",
|
||||||
|
"dateFormatUS": "年/月/日",
|
||||||
|
"timeFormat": " 時刻書式",
|
||||||
|
"timeFormatTwelveHour": "12 時間表記",
|
||||||
|
"timeFormatTwentyFourHour": "24 時間表記",
|
||||||
|
"addSelectOption": "選択候補追加",
|
||||||
|
"optionTitle": "選択候補",
|
||||||
|
"addOption": "選択候補追加",
|
||||||
|
"editProperty": "プロパティの編集"
|
||||||
|
},
|
||||||
|
"row": {
|
||||||
|
"duplicate": "コピーを作成",
|
||||||
|
"delete": "削除",
|
||||||
|
"textPlaceholder": "空白"
|
||||||
|
},
|
||||||
|
"selectOption": {
|
||||||
|
"purpleColor": "紫",
|
||||||
|
"pinkColor": "ピンク",
|
||||||
|
"lightPinkColor": "ライトピンク",
|
||||||
|
"orangeColor": "オレンジ",
|
||||||
|
"yellowColor": "黄色",
|
||||||
|
"limeColor": "ライム",
|
||||||
|
"greenColor": "緑",
|
||||||
|
"aquaColor": "水色",
|
||||||
|
"blueColor": "青",
|
||||||
|
"deleteTag": "選択候補を削除",
|
||||||
|
"colorPannelTitle": "色",
|
||||||
|
"pannelTitle": "選択候補を検索 または 作成する",
|
||||||
|
"searchOption": "選択候補を検索"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,11 +7,11 @@
|
|||||||
"letsGoButtonText": "Vamos lá",
|
"letsGoButtonText": "Vamos lá",
|
||||||
"title": "Título",
|
"title": "Título",
|
||||||
"signUp": {
|
"signUp": {
|
||||||
"buttonText": "Inscreve-se",
|
"buttonText": "Se inscreva",
|
||||||
"title": "Inscrever-se @:appName",
|
"title": "Se inscreva no @:appName",
|
||||||
"getStartedText": "Começar",
|
"getStartedText": "Começar",
|
||||||
"emptyPasswordError": "Senha não pode ser em branco.",
|
"emptyPasswordError": "Senha não pode estar em branco.",
|
||||||
"repeatPasswordEmptyError": "Confirmar a senha não pode ser em branco.",
|
"repeatPasswordEmptyError": "Confirmar a senha não pode estar em branco.",
|
||||||
"unmatchedPasswordError": "As senhas não conferem.",
|
"unmatchedPasswordError": "As senhas não conferem.",
|
||||||
"alreadyHaveAnAccount": "Já possui uma conta?",
|
"alreadyHaveAnAccount": "Já possui uma conta?",
|
||||||
"emailHint": "Email",
|
"emailHint": "Email",
|
||||||
@ -19,14 +19,14 @@
|
|||||||
"repeatPasswordHint": "Confirme a senha"
|
"repeatPasswordHint": "Confirme a senha"
|
||||||
},
|
},
|
||||||
"signIn": {
|
"signIn": {
|
||||||
"loginTitle": "Login to @:appName",
|
"loginTitle": "Entre no @:appName",
|
||||||
"loginButtonText": "Login",
|
"loginButtonText": "Login",
|
||||||
"buttonText": "Entre",
|
"buttonText": "Entre",
|
||||||
"forgotPassword": "Esqueceu a senha?",
|
"forgotPassword": "Esqueceu a senha?",
|
||||||
"emailHint": "Email",
|
"emailHint": "Email",
|
||||||
"passwordHint": "Senha",
|
"passwordHint": "Senha",
|
||||||
"dontHaveAnAccount": "Não possui uma conta?",
|
"dontHaveAnAccount": "Não possui uma conta?",
|
||||||
"repeatPasswordEmptyError": "Confirmar a senha não pode ser em branco.",
|
"repeatPasswordEmptyError": "Confirmar a senha não pode estar em branco.",
|
||||||
"unmatchedPasswordError": "As senhas não conferem."
|
"unmatchedPasswordError": "As senhas não conferem."
|
||||||
},
|
},
|
||||||
"workspace": {
|
"workspace": {
|
||||||
@ -67,7 +67,7 @@
|
|||||||
"whatsNew": "O que há de novo?",
|
"whatsNew": "O que há de novo?",
|
||||||
"help": "Ajuda & Suporte",
|
"help": "Ajuda & Suporte",
|
||||||
"debug": {
|
"debug": {
|
||||||
"name": "Informação de debug",
|
"name": "Informação de depuração",
|
||||||
"success": "Copiar informação de debug para o clipboard!",
|
"success": "Copiar informação de debug para o clipboard!",
|
||||||
"fail": "Falha em copiar a informação de debug para o clipboard"
|
"fail": "Falha em copiar a informação de debug para o clipboard"
|
||||||
}
|
}
|
||||||
@ -104,7 +104,7 @@
|
|||||||
},
|
},
|
||||||
"button": {
|
"button": {
|
||||||
"OK": "OK",
|
"OK": "OK",
|
||||||
"Cancel": "Canelar",
|
"Cancel": "Cancelar",
|
||||||
"signIn": "Entrar",
|
"signIn": "Entrar",
|
||||||
"signOut": "Sair",
|
"signOut": "Sair",
|
||||||
"complete": "Completar",
|
"complete": "Completar",
|
||||||
@ -143,4 +143,5 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
146
frontend/app_flowy/assets/translations/pt-PT.json
Normal file
146
frontend/app_flowy/assets/translations/pt-PT.json
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
{
|
||||||
|
"appName": "AppFlowy",
|
||||||
|
"defaultUsername": "Me",
|
||||||
|
"welcomeText": "Bem vindo ao @:appName",
|
||||||
|
"githubStarText": "Star on GitHub",
|
||||||
|
"subscribeNewsletterText": "Inscreve-te ao Newsletter",
|
||||||
|
"letsGoButtonText": "Bora",
|
||||||
|
"title": "Título",
|
||||||
|
"signUp": {
|
||||||
|
"buttonText": "Inscreve-te",
|
||||||
|
"title": "Inscreve-te ao @:appName",
|
||||||
|
"getStartedText": "Começar",
|
||||||
|
"emptyPasswordError": "A palavra-passe não pode estar em branco.",
|
||||||
|
"repeatPasswordEmptyError": "Confirmar a palavra-passe não pode estar em branco.",
|
||||||
|
"unmatchedPasswordError": "As palavras-passes não coincidem.",
|
||||||
|
"alreadyHaveAnAccount": "Já possuis uma conta?",
|
||||||
|
"emailHint": "Email",
|
||||||
|
"passwordHint": "Password",
|
||||||
|
"repeatPasswordHint": "Confirma a tua password"
|
||||||
|
},
|
||||||
|
"signIn": {
|
||||||
|
"loginTitle": "Entre no @:appName",
|
||||||
|
"loginButtonText": "Login",
|
||||||
|
"buttonText": "Entre",
|
||||||
|
"forgotPassword": "Esqueceste-te da tua palavra-passe?",
|
||||||
|
"emailHint": "Email",
|
||||||
|
"passwordHint": "Palavra-passe",
|
||||||
|
"dontHaveAnAccount": "Não possuis uma conta?",
|
||||||
|
"repeatPasswordEmptyError": "Confirmar a palavra-passe não pode estar em branco.",
|
||||||
|
"unmatchedPasswordError": "As palavras-passes não conferem."
|
||||||
|
},
|
||||||
|
"workspace": {
|
||||||
|
"create": "Cria um ambiente de trabalho",
|
||||||
|
"hint": "ambiente de trabalho",
|
||||||
|
"notFoundError": "Ambiente de trabalho não encontrada"
|
||||||
|
},
|
||||||
|
"shareAction": {
|
||||||
|
"buttonText": "Partilhar",
|
||||||
|
"workInProgress": "Em breve",
|
||||||
|
"markdown": "Markdown",
|
||||||
|
"copyLink": "Copiar o link"
|
||||||
|
},
|
||||||
|
"disclosureAction": {
|
||||||
|
"rename": "Renomear",
|
||||||
|
"delete": "Apagar",
|
||||||
|
"duplicate": "Duplicar"
|
||||||
|
},
|
||||||
|
"blankPageTitle": "Página em branco",
|
||||||
|
"newPageText": "Nova página",
|
||||||
|
"trash": {
|
||||||
|
"text": "Lixo",
|
||||||
|
"restoreAll": "Restaurar todos",
|
||||||
|
"deleteAll": "Apagar todos",
|
||||||
|
"pageHeader": {
|
||||||
|
"fileName": "Nome do ficheiro",
|
||||||
|
"lastModified": "Última modificação",
|
||||||
|
"created": "Criado"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deletePagePrompt": {
|
||||||
|
"text": "Esta página está no lixo",
|
||||||
|
"restore": "Restaurar a página",
|
||||||
|
"deletePermanent": "Apagar permanentemente"
|
||||||
|
},
|
||||||
|
"dialogCreatePageNameHint": "Nome da página",
|
||||||
|
"questionBubble": {
|
||||||
|
"whatsNew": "O que há de novo?",
|
||||||
|
"help": "Ajuda & Suporte",
|
||||||
|
"debug": {
|
||||||
|
"name": "Informação de depuração",
|
||||||
|
"success": "Copiar informação de depuração para o clipboard!",
|
||||||
|
"fail": "Falha em copiar a informação de depuração para o clipboard"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"menuAppHeader": {
|
||||||
|
"addPageTooltip": "Adiciona uma nova página.",
|
||||||
|
"defaultNewPageName": "Sem título",
|
||||||
|
"renameDialog": "Renomear"
|
||||||
|
},
|
||||||
|
"toolbar": {
|
||||||
|
"undo": "Desfazer",
|
||||||
|
"redo": "Refazer",
|
||||||
|
"bold": "Negrito",
|
||||||
|
"italic": "Itálico",
|
||||||
|
"underline": "Sublinhado",
|
||||||
|
"strike": "Riscado",
|
||||||
|
"numList": "Lista numerada",
|
||||||
|
"bulletList": "Lista com marcadores",
|
||||||
|
"checkList": "Lista de verificação",
|
||||||
|
"inlineCode": "Embutir código",
|
||||||
|
"quote": "Citação em bloco",
|
||||||
|
"header": "Cabeçalho",
|
||||||
|
"highlight": "Realçar"
|
||||||
|
},
|
||||||
|
"tooltip": {
|
||||||
|
"lightMode": "Mudar para o modo Claro.",
|
||||||
|
"darkMode": "Mudar para o modo Escuro."
|
||||||
|
},
|
||||||
|
"contactsPage": {
|
||||||
|
"title": "Conctatos",
|
||||||
|
"whatsHappening": "O que está a acontecer nesta semana?",
|
||||||
|
"addContact": "Adicionar um conctato",
|
||||||
|
"editContact": "Editar um conctato"
|
||||||
|
},
|
||||||
|
"button": {
|
||||||
|
"OK": "OK",
|
||||||
|
"Cancel": "Cancelar",
|
||||||
|
"signIn": "Entrar",
|
||||||
|
"signOut": "Sair",
|
||||||
|
"complete": "Completar",
|
||||||
|
"save": "Guardar"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"welcome": "Bem vindo!",
|
||||||
|
"firstName": "Nome",
|
||||||
|
"middleName": "Nome do Meio",
|
||||||
|
"lastName": "Apelido",
|
||||||
|
"stepX": "Passo {X}"
|
||||||
|
},
|
||||||
|
"oAuth": {
|
||||||
|
"err": {
|
||||||
|
"failedTitle": "Erro ao conectar à sua conta.",
|
||||||
|
"failedMsg": "Verifica se concluiste o processo de login no teu navegador."
|
||||||
|
},
|
||||||
|
"google": {
|
||||||
|
"title": "GOOGLE SIGN-IN",
|
||||||
|
"instruction1": "Para importar os teus Conctatos do Google, tens de autorizar esta aplicação usando o teu navegador web.",
|
||||||
|
"instruction2": "Copia este código para a tua área de transferências clicando no ícone ou selecionando o texto:",
|
||||||
|
"instruction3": "Navega até o link a seguir no seu navegador e digite o código acima:",
|
||||||
|
"instruction4": "Clica no botão abaixo ao concluir a inscrição:"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"title": "Definições",
|
||||||
|
"menu": {
|
||||||
|
"appearance": "Aparência",
|
||||||
|
"language": "Idioma",
|
||||||
|
"open": "Abrir as Definições"
|
||||||
|
},
|
||||||
|
"appearance": {
|
||||||
|
"lightLabel": "Modo Claro",
|
||||||
|
"darkLabel": "Modo Escuro"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -141,6 +141,68 @@
|
|||||||
"lightLabel": "Светлая тема",
|
"lightLabel": "Светлая тема",
|
||||||
"darkLabel": "Тёмная тема"
|
"darkLabel": "Тёмная тема"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"grid": {
|
||||||
|
"settings": {
|
||||||
|
"filter": "Фильтр",
|
||||||
|
"sortBy": "Сортировать",
|
||||||
|
"Properties": "Свойства"
|
||||||
|
},
|
||||||
|
"field": {
|
||||||
|
"hide": "Скрыть",
|
||||||
|
"insertLeft": "Вставить слева",
|
||||||
|
"insertRight": "Вставить справа",
|
||||||
|
"duplicate": "Дублировать",
|
||||||
|
"delete": "Удалить",
|
||||||
|
"textFieldName": "Текст",
|
||||||
|
"checkboxFieldName": "Checkbox",
|
||||||
|
"dateFieldName": "Дата",
|
||||||
|
"numberFieldName": "Число",
|
||||||
|
"singleSelectFieldName": "Выбор",
|
||||||
|
"multiSelectFieldName": "Выбор многих",
|
||||||
|
"urlFieldName": "URL",
|
||||||
|
"numberFormat": " Формат числа",
|
||||||
|
"dateFormat": " Формат даты",
|
||||||
|
"includeTime": " Время",
|
||||||
|
"dateFormatFriendly": "День Месяц, Год",
|
||||||
|
"dateFormatISO": "Год-Месяц-День",
|
||||||
|
"dateFormatLocal": "Год/Месяц/День",
|
||||||
|
"dateFormatUS": "Год/Месяц/День",
|
||||||
|
"timeFormat": " Форматировать время",
|
||||||
|
"invalidTimeFormat": "Неверный формат",
|
||||||
|
"timeFormatTwelveHour": "12 часов",
|
||||||
|
"timeFormatTwentyFourHour": "24 часа",
|
||||||
|
"addSelectOption": "Добавить вариант",
|
||||||
|
"optionTitle": "Варианты",
|
||||||
|
"addOption": "Добавить",
|
||||||
|
"editProperty": "Редактировать свойство"
|
||||||
|
},
|
||||||
|
"row": {
|
||||||
|
"duplicate": "Дублировать",
|
||||||
|
"delete": "Удалить",
|
||||||
|
"textPlaceholder": "Пусто",
|
||||||
|
"copyProperty": "Свойство скопировано"
|
||||||
|
},
|
||||||
|
"selectOption": {
|
||||||
|
"create": "Создать",
|
||||||
|
"purpleColor": "Фиолетовый",
|
||||||
|
"pinkColor": "Розовый",
|
||||||
|
"lightPinkColor": "Светло-розовый",
|
||||||
|
"orangeColor": "Оранжевый",
|
||||||
|
"yellowColor": "Желтый",
|
||||||
|
"limeColor": "Ярко-зелёный",
|
||||||
|
"greenColor": "Зелёный",
|
||||||
|
"aquaColor": "Морской волны",
|
||||||
|
"blueColor": "Синий",
|
||||||
|
"deleteTag": "Удалить вариант",
|
||||||
|
"colorPannelTitle": "Цвета",
|
||||||
|
"pannelTitle": "Выберите или создайте вариант",
|
||||||
|
"searchOption": "Поиск"
|
||||||
|
},
|
||||||
|
"date": {
|
||||||
|
"timeHintTextInTwelveHour": "12:00 AM",
|
||||||
|
"timeHintTextInTwentyFourHour": "12:00"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
39
frontend/app_flowy/lib/core/folder_notification.dart
Normal file
39
frontend/app_flowy/lib/core/folder_notification.dart
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:flowy_sdk/protobuf/dart-notify/protobuf.dart';
|
||||||
|
import 'package:dartz/dartz.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/rust_stream.dart';
|
||||||
|
|
||||||
|
import 'notification_helper.dart';
|
||||||
|
|
||||||
|
// Folder
|
||||||
|
typedef FolderNotificationCallback = void Function(FolderNotification, Either<Uint8List, FlowyError>);
|
||||||
|
|
||||||
|
class FolderNotificationParser extends NotificationParser<FolderNotification, FlowyError> {
|
||||||
|
FolderNotificationParser({String? id, required FolderNotificationCallback callback})
|
||||||
|
: super(
|
||||||
|
id: id,
|
||||||
|
callback: callback,
|
||||||
|
tyParser: (ty) => FolderNotification.valueOf(ty),
|
||||||
|
errorParser: (bytes) => FlowyError.fromBuffer(bytes),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef FolderNotificationHandler = Function(FolderNotification ty, Either<Uint8List, FlowyError> result);
|
||||||
|
|
||||||
|
class FolderNotificationListener {
|
||||||
|
StreamSubscription<SubscribeObject>? _subscription;
|
||||||
|
FolderNotificationParser? _parser;
|
||||||
|
|
||||||
|
FolderNotificationListener({required String objectId, required FolderNotificationHandler handler})
|
||||||
|
: _parser = FolderNotificationParser(id: objectId, callback: handler) {
|
||||||
|
_subscription = RustStreamReceiver.listen((observable) => _parser?.parse(observable));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> stop() async {
|
||||||
|
_parser = null;
|
||||||
|
await _subscription?.cancel();
|
||||||
|
}
|
||||||
|
}
|
39
frontend/app_flowy/lib/core/grid_notification.dart
Normal file
39
frontend/app_flowy/lib/core/grid_notification.dart
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:flowy_sdk/protobuf/dart-notify/protobuf.dart';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||||
|
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
|
||||||
|
import 'package:flowy_sdk/rust_stream.dart';
|
||||||
|
|
||||||
|
import 'notification_helper.dart';
|
||||||
|
|
||||||
|
// GridPB
|
||||||
|
typedef GridNotificationCallback = void Function(GridNotification, Either<Uint8List, FlowyError>);
|
||||||
|
|
||||||
|
class GridNotificationParser extends NotificationParser<GridNotification, FlowyError> {
|
||||||
|
GridNotificationParser({String? id, required GridNotificationCallback callback})
|
||||||
|
: super(
|
||||||
|
id: id,
|
||||||
|
callback: callback,
|
||||||
|
tyParser: (ty) => GridNotification.valueOf(ty),
|
||||||
|
errorParser: (bytes) => FlowyError.fromBuffer(bytes),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef GridNotificationHandler = Function(GridNotification ty, Either<Uint8List, FlowyError> result);
|
||||||
|
|
||||||
|
class GridNotificationListener {
|
||||||
|
StreamSubscription<SubscribeObject>? _subscription;
|
||||||
|
GridNotificationParser? _parser;
|
||||||
|
|
||||||
|
GridNotificationListener({required String objectId, required GridNotificationHandler handler})
|
||||||
|
: _parser = GridNotificationParser(id: objectId, callback: handler) {
|
||||||
|
_subscription = RustStreamReceiver.listen((observable) => _parser?.parse(observable));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> stop() async {
|
||||||
|
_parser = null;
|
||||||
|
await _subscription?.cancel();
|
||||||
|
}
|
||||||
|
}
|
@ -1,68 +1,6 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:flowy_sdk/protobuf/dart-notify/protobuf.dart';
|
import 'package:flowy_sdk/protobuf/dart-notify/protobuf.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart';
|
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.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-grid/dart_notification.pb.dart';
|
|
||||||
import 'package:flowy_sdk/rust_stream.dart';
|
|
||||||
|
|
||||||
// User
|
|
||||||
typedef UserNotificationCallback = void Function(UserNotification, Either<Uint8List, FlowyError>);
|
|
||||||
|
|
||||||
class UserNotificationParser extends NotificationParser<UserNotification, FlowyError> {
|
|
||||||
UserNotificationParser({required String id, required UserNotificationCallback callback})
|
|
||||||
: super(
|
|
||||||
id: id,
|
|
||||||
callback: callback,
|
|
||||||
tyParser: (ty) => UserNotification.valueOf(ty),
|
|
||||||
errorParser: (bytes) => FlowyError.fromBuffer(bytes),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Folder
|
|
||||||
typedef FolderNotificationCallback = void Function(FolderNotification, Either<Uint8List, FlowyError>);
|
|
||||||
|
|
||||||
class FolderNotificationParser extends NotificationParser<FolderNotification, FlowyError> {
|
|
||||||
FolderNotificationParser({String? id, required FolderNotificationCallback callback})
|
|
||||||
: super(
|
|
||||||
id: id,
|
|
||||||
callback: callback,
|
|
||||||
tyParser: (ty) => FolderNotification.valueOf(ty),
|
|
||||||
errorParser: (bytes) => FlowyError.fromBuffer(bytes),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grid
|
|
||||||
typedef GridNotificationCallback = void Function(GridNotification, Either<Uint8List, FlowyError>);
|
|
||||||
|
|
||||||
class GridNotificationParser extends NotificationParser<GridNotification, FlowyError> {
|
|
||||||
GridNotificationParser({String? id, required GridNotificationCallback callback})
|
|
||||||
: super(
|
|
||||||
id: id,
|
|
||||||
callback: callback,
|
|
||||||
tyParser: (ty) => GridNotification.valueOf(ty),
|
|
||||||
errorParser: (bytes) => FlowyError.fromBuffer(bytes),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef GridNotificationHandler = Function(GridNotification ty, Either<Uint8List, FlowyError> result);
|
|
||||||
|
|
||||||
class GridNotificationListener {
|
|
||||||
StreamSubscription<SubscribeObject>? _subscription;
|
|
||||||
GridNotificationParser? _parser;
|
|
||||||
|
|
||||||
GridNotificationListener({required String objectId, required GridNotificationHandler handler})
|
|
||||||
: _parser = GridNotificationParser(id: objectId, callback: handler) {
|
|
||||||
_subscription = RustStreamReceiver.listen((observable) => _parser?.parse(observable));
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> stop() async {
|
|
||||||
_parser = null;
|
|
||||||
await _subscription?.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NotificationParser<T, E> {
|
class NotificationParser<T, E> {
|
||||||
String? id;
|
String? id;
|
||||||
|
39
frontend/app_flowy/lib/core/user_notification.dart
Normal file
39
frontend/app_flowy/lib/core/user_notification.dart
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:flowy_sdk/protobuf/dart-notify/protobuf.dart';
|
||||||
|
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||||
|
import 'package:flowy_sdk/rust_stream.dart';
|
||||||
|
|
||||||
|
import 'notification_helper.dart';
|
||||||
|
|
||||||
|
// User
|
||||||
|
typedef UserNotificationCallback = void Function(UserNotification, Either<Uint8List, FlowyError>);
|
||||||
|
|
||||||
|
class UserNotificationParser extends NotificationParser<UserNotification, FlowyError> {
|
||||||
|
UserNotificationParser({required String id, required UserNotificationCallback callback})
|
||||||
|
: super(
|
||||||
|
id: id,
|
||||||
|
callback: callback,
|
||||||
|
tyParser: (ty) => UserNotification.valueOf(ty),
|
||||||
|
errorParser: (bytes) => FlowyError.fromBuffer(bytes),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef UserNotificationHandler = Function(UserNotification ty, Either<Uint8List, FlowyError> result);
|
||||||
|
|
||||||
|
class UserNotificationListener {
|
||||||
|
StreamSubscription<SubscribeObject>? _subscription;
|
||||||
|
UserNotificationParser? _parser;
|
||||||
|
|
||||||
|
UserNotificationListener({required String objectId, required UserNotificationHandler handler})
|
||||||
|
: _parser = UserNotificationParser(id: objectId, callback: handler) {
|
||||||
|
_subscription = RustStreamReceiver.listen((observable) => _parser?.parse(observable));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> stop() async {
|
||||||
|
_parser = null;
|
||||||
|
await _subscription?.cancel();
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@ import 'package:app_flowy/plugin/plugin.dart';
|
|||||||
import 'package:app_flowy/startup/startup.dart';
|
import 'package:app_flowy/startup/startup.dart';
|
||||||
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
||||||
import 'package:flowy_infra/notifier.dart';
|
import 'package:flowy_infra/notifier.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
export "./src/sandbox.dart";
|
export "./src/sandbox.dart";
|
||||||
@ -14,6 +14,7 @@ enum DefaultPlugin {
|
|||||||
blank,
|
blank,
|
||||||
trash,
|
trash,
|
||||||
grid,
|
grid,
|
||||||
|
board,
|
||||||
}
|
}
|
||||||
|
|
||||||
extension FlowyDefaultPluginExt on DefaultPlugin {
|
extension FlowyDefaultPluginExt on DefaultPlugin {
|
||||||
@ -27,6 +28,8 @@ extension FlowyDefaultPluginExt on DefaultPlugin {
|
|||||||
return 2;
|
return 2;
|
||||||
case DefaultPlugin.grid:
|
case DefaultPlugin.grid:
|
||||||
return 3;
|
return 3;
|
||||||
|
case DefaultPlugin.board:
|
||||||
|
return 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,9 @@ import 'package:app_flowy/user/application/prelude.dart';
|
|||||||
import 'package:app_flowy/user/presentation/router.dart';
|
import 'package:app_flowy/user/presentation/router.dart';
|
||||||
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
||||||
import 'package:app_flowy/workspace/presentation/home/menu/menu.dart';
|
import 'package:app_flowy/workspace/presentation/home/menu/menu.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-user-data-model/user_profile.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
|
|
||||||
@ -51,37 +51,37 @@ void _resolveHomeDeps(GetIt getIt) {
|
|||||||
|
|
||||||
getIt.registerSingleton(MenuSharedState());
|
getIt.registerSingleton(MenuSharedState());
|
||||||
|
|
||||||
getIt.registerFactoryParam<UserListener, UserProfile, void>(
|
getIt.registerFactoryParam<UserListener, UserProfilePB, void>(
|
||||||
(user, _) => UserListener(user: 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(),
|
userService: UserService(userId: user.id),
|
||||||
userListener: getIt<UserListener>(param1: user),
|
userWorkspaceListener: UserWorkspaceListener(userProfile: user),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 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>((user, workspaceId) =>
|
getIt.registerFactoryParam<WorkspaceListener, UserProfilePB, String>(
|
||||||
WorkspaceListener(service: WorkspaceListenerService(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(),
|
||||||
@ -90,23 +90,19 @@ 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, _) => MenuUserBloc(user),
|
||||||
user,
|
|
||||||
UserService(),
|
|
||||||
getIt<UserListener>(param1: 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),
|
||||||
@ -127,7 +123,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(),
|
||||||
@ -138,8 +134,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),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -157,31 +153,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,
|
||||||
|
@ -37,6 +37,7 @@ class InitAppWidgetTask extends LaunchTask {
|
|||||||
Locale('fr', 'CA'),
|
Locale('fr', 'CA'),
|
||||||
Locale('hu', 'HU'),
|
Locale('hu', 'HU'),
|
||||||
Locale('it', 'IT'),
|
Locale('it', 'IT'),
|
||||||
|
Locale('ja', 'JP'),
|
||||||
Locale('pt', 'BR'),
|
Locale('pt', 'BR'),
|
||||||
Locale('ru', 'RU'),
|
Locale('ru', 'RU'),
|
||||||
Locale('tr', 'TR'),
|
Locale('tr', 'TR'),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:app_flowy/plugin/plugin.dart';
|
import 'package:app_flowy/plugin/plugin.dart';
|
||||||
import 'package:app_flowy/startup/startup.dart';
|
import 'package:app_flowy/startup/startup.dart';
|
||||||
import 'package:app_flowy/workspace/presentation/plugins/blank/blank.dart';
|
import 'package:app_flowy/workspace/presentation/plugins/blank/blank.dart';
|
||||||
|
import 'package:app_flowy/workspace/presentation/plugins/board/board.dart';
|
||||||
import 'package:app_flowy/workspace/presentation/plugins/doc/document.dart';
|
import 'package:app_flowy/workspace/presentation/plugins/doc/document.dart';
|
||||||
import 'package:app_flowy/workspace/presentation/plugins/grid/grid.dart';
|
import 'package:app_flowy/workspace/presentation/plugins/grid/grid.dart';
|
||||||
import 'package:app_flowy/workspace/presentation/plugins/trash/trash.dart';
|
import 'package:app_flowy/workspace/presentation/plugins/trash/trash.dart';
|
||||||
@ -15,5 +16,6 @@ class PluginLoadTask extends LaunchTask {
|
|||||||
registerPlugin(builder: TrashPluginBuilder(), config: TrashPluginConfig());
|
registerPlugin(builder: TrashPluginBuilder(), config: TrashPluginConfig());
|
||||||
registerPlugin(builder: DocumentPluginBuilder());
|
registerPlugin(builder: DocumentPluginBuilder());
|
||||||
registerPlugin(builder: GridPluginBuilder(), config: GridPluginConfig());
|
registerPlugin(builder: GridPluginBuilder(), config: GridPluginConfig());
|
||||||
|
registerPlugin(builder: BoardPluginBuilder(), config: BoardPluginConfig());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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-data-model/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 ?? '';
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import 'package:app_flowy/user/application/auth_service.dart';
|
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/errors.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-user-data-model/protobuf.dart' show UserProfile, ErrorCode;
|
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';
|
||||||
|
|
||||||
@ -68,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(
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import 'package:app_flowy/user/application/auth_service.dart';
|
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-user-data-model/protobuf.dart' show UserProfile, ErrorCode;
|
import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.dart';
|
||||||
|
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';
|
||||||
@ -119,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(
|
||||||
|
@ -1,117 +1,72 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'package:app_flowy/core/folder_notification.dart';
|
||||||
|
import 'package:app_flowy/core/user_notification.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/workspace.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-error-code/code.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 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:app_flowy/core/notification_helper.dart';
|
|
||||||
import 'package:flowy_infra/notifier.dart';
|
import 'package:flowy_infra/notifier.dart';
|
||||||
import 'package:flowy_sdk/protobuf/dart-notify/protobuf.dart';
|
import 'package:flowy_sdk/protobuf/dart-notify/protobuf.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/protobuf/flowy-user-data-model/errors.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-user-data-model/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>;
|
||||||
typedef WorkspaceListNotifyValue = Either<List<Workspace>, FlowyError>;
|
|
||||||
typedef WorkspaceSettingNotifyValue = Either<CurrentWorkspaceSetting, FlowyError>;
|
|
||||||
|
|
||||||
class UserListener {
|
class UserListener {
|
||||||
StreamSubscription<SubscribeObject>? _subscription;
|
StreamSubscription<SubscribeObject>? _subscription;
|
||||||
final _profileNotifier = PublishNotifier<UserProfileNotifyValue>();
|
PublishNotifier<AuthNotifyValue>? _authNotifier = PublishNotifier();
|
||||||
final _authNotifier = PublishNotifier<AuthNotifyValue>();
|
PublishNotifier<UserProfileNotifyValue>? _profileNotifier = PublishNotifier();
|
||||||
final _workspaceListNotifier = PublishNotifier<WorkspaceListNotifyValue>();
|
|
||||||
final _workSettingNotifier = PublishNotifier<WorkspaceSettingNotifyValue>();
|
|
||||||
|
|
||||||
FolderNotificationParser? _workspaceParser;
|
|
||||||
UserNotificationParser? _userParser;
|
UserNotificationParser? _userParser;
|
||||||
final UserProfile _user;
|
final UserProfilePB _userProfile;
|
||||||
UserListener({
|
UserListener({
|
||||||
required UserProfile user,
|
required UserProfilePB userProfile,
|
||||||
}) : _user = user;
|
}) : _userProfile = userProfile;
|
||||||
|
|
||||||
void start({
|
void start({
|
||||||
void Function(AuthNotifyValue)? onAuthChanged,
|
void Function(AuthNotifyValue)? onAuthChanged,
|
||||||
void Function(UserProfileNotifyValue)? onProfileUpdated,
|
void Function(UserProfileNotifyValue)? onProfileUpdated,
|
||||||
void Function(WorkspaceListNotifyValue)? onWorkspaceListUpdated,
|
|
||||||
void Function(WorkspaceSettingNotifyValue)? onWorkspaceSettingUpdated,
|
|
||||||
}) {
|
}) {
|
||||||
if (onAuthChanged != null) {
|
|
||||||
_authNotifier.addListener(() {
|
|
||||||
onAuthChanged(_authNotifier.currentValue!);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onProfileUpdated != null) {
|
if (onProfileUpdated != null) {
|
||||||
_profileNotifier.addListener(() {
|
_profileNotifier?.addPublishListener(onProfileUpdated);
|
||||||
onProfileUpdated(_profileNotifier.currentValue!);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onWorkspaceListUpdated != null) {
|
if (onAuthChanged != null) {
|
||||||
_workspaceListNotifier.addListener(() {
|
_authNotifier?.addPublishListener(onAuthChanged);
|
||||||
onWorkspaceListUpdated(_workspaceListNotifier.currentValue!);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onWorkspaceSettingUpdated != null) {
|
_userParser = UserNotificationParser(id: _userProfile.token, callback: _userNotificationCallback);
|
||||||
_workSettingNotifier.addListener(() {
|
|
||||||
onWorkspaceSettingUpdated(_workSettingNotifier.currentValue!);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_workspaceParser = FolderNotificationParser(id: _user.token, callback: _notificationCallback);
|
|
||||||
_userParser = UserNotificationParser(id: _user.token, callback: _userNotificationCallback);
|
|
||||||
_subscription = RustStreamReceiver.listen((observable) {
|
_subscription = RustStreamReceiver.listen((observable) {
|
||||||
_workspaceParser?.parse(observable);
|
|
||||||
_userParser?.parse(observable);
|
_userParser?.parse(observable);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> stop() async {
|
Future<void> stop() async {
|
||||||
_workspaceParser = null;
|
|
||||||
_userParser = null;
|
_userParser = null;
|
||||||
await _subscription?.cancel();
|
await _subscription?.cancel();
|
||||||
_profileNotifier.dispose();
|
_profileNotifier?.dispose();
|
||||||
_authNotifier.dispose();
|
_profileNotifier = null;
|
||||||
_workspaceListNotifier.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _notificationCallback(FolderNotification ty, Either<Uint8List, FlowyError> result) {
|
_authNotifier?.dispose();
|
||||||
switch (ty) {
|
_authNotifier = null;
|
||||||
case FolderNotification.UserCreateWorkspace:
|
|
||||||
case FolderNotification.UserDeleteWorkspace:
|
|
||||||
case FolderNotification.WorkspaceListUpdated:
|
|
||||||
result.fold(
|
|
||||||
(payload) => _workspaceListNotifier.value = left(RepeatedWorkspace.fromBuffer(payload).items),
|
|
||||||
(error) => _workspaceListNotifier.value = right(error),
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case FolderNotification.WorkspaceSetting:
|
|
||||||
result.fold(
|
|
||||||
(payload) => _workSettingNotifier.value = left(CurrentWorkspaceSetting.fromBuffer(payload)),
|
|
||||||
(error) => _workSettingNotifier.value = right(error),
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case FolderNotification.UserUnauthorized:
|
|
||||||
result.fold(
|
|
||||||
(_) {},
|
|
||||||
(error) => _authNotifier.value = right(FlowyError.create()..code = ErrorCode.UserUnauthorized.value),
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _userNotificationCallback(user.UserNotification ty, Either<Uint8List, FlowyError> result) {
|
void _userNotificationCallback(user.UserNotification ty, Either<Uint8List, FlowyError> result) {
|
||||||
switch (ty) {
|
switch (ty) {
|
||||||
case user.UserNotification.UserUnauthorized:
|
case user.UserNotification.UserUnauthorized:
|
||||||
result.fold(
|
result.fold(
|
||||||
(payload) => _profileNotifier.value = left(UserProfile.fromBuffer(payload)),
|
(_) {},
|
||||||
(error) => _profileNotifier.value = right(error),
|
(error) => _authNotifier?.value = right(error),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case user.UserNotification.UserProfileUpdated:
|
||||||
|
result.fold(
|
||||||
|
(payload) => _profileNotifier?.value = left(UserProfilePB.fromBuffer(payload)),
|
||||||
|
(error) => _profileNotifier?.value = right(error),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -119,3 +74,81 @@ class UserListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef WorkspaceListNotifyValue = Either<List<WorkspacePB>, FlowyError>;
|
||||||
|
typedef WorkspaceSettingNotifyValue = Either<CurrentWorkspaceSettingPB, FlowyError>;
|
||||||
|
|
||||||
|
class UserWorkspaceListener {
|
||||||
|
PublishNotifier<AuthNotifyValue>? _authNotifier = PublishNotifier();
|
||||||
|
PublishNotifier<WorkspaceListNotifyValue>? _workspacesChangedNotifier = PublishNotifier();
|
||||||
|
PublishNotifier<WorkspaceSettingNotifyValue>? _settingChangedNotifier = PublishNotifier();
|
||||||
|
|
||||||
|
FolderNotificationListener? _listener;
|
||||||
|
final UserProfilePB _userProfile;
|
||||||
|
|
||||||
|
UserWorkspaceListener({
|
||||||
|
required UserProfilePB userProfile,
|
||||||
|
}) : _userProfile = userProfile;
|
||||||
|
|
||||||
|
void start({
|
||||||
|
void Function(AuthNotifyValue)? onAuthChanged,
|
||||||
|
void Function(WorkspaceListNotifyValue)? onWorkspacesUpdated,
|
||||||
|
void Function(WorkspaceSettingNotifyValue)? onSettingUpdated,
|
||||||
|
}) {
|
||||||
|
if (onAuthChanged != null) {
|
||||||
|
_authNotifier?.addPublishListener(onAuthChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onWorkspacesUpdated != null) {
|
||||||
|
_workspacesChangedNotifier?.addPublishListener(onWorkspacesUpdated);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onSettingUpdated != null) {
|
||||||
|
_settingChangedNotifier?.addPublishListener(onSettingUpdated);
|
||||||
|
}
|
||||||
|
|
||||||
|
_listener = FolderNotificationListener(
|
||||||
|
objectId: _userProfile.token,
|
||||||
|
handler: _handleObservableType,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleObservableType(FolderNotification ty, Either<Uint8List, FlowyError> result) {
|
||||||
|
switch (ty) {
|
||||||
|
case FolderNotification.UserCreateWorkspace:
|
||||||
|
case FolderNotification.UserDeleteWorkspace:
|
||||||
|
case FolderNotification.WorkspaceListUpdated:
|
||||||
|
result.fold(
|
||||||
|
(payload) => _workspacesChangedNotifier?.value = left(RepeatedWorkspacePB.fromBuffer(payload).items),
|
||||||
|
(error) => _workspacesChangedNotifier?.value = right(error),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case FolderNotification.WorkspaceSetting:
|
||||||
|
result.fold(
|
||||||
|
(payload) => _settingChangedNotifier?.value = left(CurrentWorkspaceSettingPB.fromBuffer(payload)),
|
||||||
|
(error) => _settingChangedNotifier?.value = right(error),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case FolderNotification.UserUnauthorized:
|
||||||
|
result.fold(
|
||||||
|
(_) {},
|
||||||
|
(error) => _authNotifier?.value = right(FlowyError.create()..code = ErrorCode.UserUnauthorized.value),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> stop() async {
|
||||||
|
await _listener?.stop();
|
||||||
|
_workspacesChangedNotifier?.dispose();
|
||||||
|
_workspacesChangedNotifier = null;
|
||||||
|
|
||||||
|
_settingChangedNotifier?.dispose();
|
||||||
|
_settingChangedNotifier = null;
|
||||||
|
|
||||||
|
_authNotifier?.dispose();
|
||||||
|
_authNotifier = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,15 +1,42 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
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-folder-data-model/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-user-data-model/user_profile.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder/workspace.pb.dart';
|
||||||
|
import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart';
|
||||||
|
|
||||||
class UserService {
|
class UserService {
|
||||||
Future<Either<UserProfile, FlowyError>> fetchUserProfile({required String userId}) {
|
final String userId;
|
||||||
|
UserService({
|
||||||
|
required this.userId,
|
||||||
|
});
|
||||||
|
Future<Either<UserProfilePB, FlowyError>> getUserProfile({required String userId}) {
|
||||||
return UserEventGetUserProfile().send();
|
return UserEventGetUserProfile().send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Either<Unit, FlowyError>> updateUserProfile({
|
||||||
|
String? name,
|
||||||
|
String? password,
|
||||||
|
String? email,
|
||||||
|
}) {
|
||||||
|
var payload = UpdateUserProfilePayloadPB.create()..id = userId;
|
||||||
|
|
||||||
|
if (name != null) {
|
||||||
|
payload.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password != null) {
|
||||||
|
payload.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (email != null) {
|
||||||
|
payload.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
return UserEventUpdateUserProfile(payload).send();
|
||||||
|
}
|
||||||
|
|
||||||
Future<Either<Unit, FlowyError>> deleteWorkspace({required String workspaceId}) {
|
Future<Either<Unit, FlowyError>> deleteWorkspace({required String workspaceId}) {
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
@ -22,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(
|
||||||
@ -33,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),
|
||||||
@ -43,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) {
|
||||||
|
@ -2,14 +2,14 @@ import 'package:dartz/dartz.dart';
|
|||||||
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||||
import 'package:flowy_sdk/flowy_sdk.dart';
|
import 'package:flowy_sdk/flowy_sdk.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-data-model/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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import 'package:flowy_sdk/protobuf/flowy-user-data-model/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;
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,8 @@ 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-data-model/protobuf.dart' show UserProfile;
|
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfilePB;
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/protobuf.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder/protobuf.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class AuthRouter {
|
class AuthRouter {
|
||||||
@ -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),
|
||||||
|
@ -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-data-model/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),
|
||||||
|
@ -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-data-model/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),
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'package:app_flowy/user/application/auth_service.dart';
|
import 'package:app_flowy/user/application/auth_service.dart';
|
||||||
import 'package:app_flowy/user/application/user_listener.dart';
|
|
||||||
import 'package:app_flowy/user/presentation/router.dart';
|
import 'package:app_flowy/user/presentation/router.dart';
|
||||||
import 'package:app_flowy/user/presentation/widgets/background.dart';
|
import 'package:app_flowy/user/presentation/widgets/background.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
@ -10,9 +9,9 @@ import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
|||||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||||
import 'package:flowy_sdk/log.dart';
|
import 'package:flowy_sdk/log.dart';
|
||||||
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/protobuf.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder/protobuf.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-data-model/user_profile.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.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:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
@ -34,8 +33,6 @@ class SkipLogInScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _SkipLogInScreenState extends State<SkipLogInScreen> {
|
class _SkipLogInScreenState extends State<SkipLogInScreen> {
|
||||||
UserListener? userListener;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -119,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) {
|
||||||
|
@ -4,7 +4,7 @@ import 'package:app_flowy/user/domain/auth_state.dart';
|
|||||||
import 'package:app_flowy/user/presentation/router.dart';
|
import 'package:app_flowy/user/presentation/router.dart';
|
||||||
import 'package:flowy_sdk/log.dart';
|
import 'package:flowy_sdk/log.dart';
|
||||||
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||||
import 'package:flowy_sdk/protobuf/error-code/error_code.pbenum.dart';
|
import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.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';
|
||||||
|
|
||||||
|
@ -5,14 +5,14 @@ import 'package:flowy_infra/theme.dart';
|
|||||||
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
|
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/workspace.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder/workspace.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-user-data-model/user_profile.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.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: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
|
||||||
|
@ -7,8 +7,8 @@ import 'package:app_flowy/workspace/application/app/app_service.dart';
|
|||||||
import 'package:app_flowy/workspace/presentation/home/menu/menu.dart';
|
import 'package:app_flowy/workspace/presentation/home/menu/menu.dart';
|
||||||
import 'package:expandable/expandable.dart';
|
import 'package:expandable/expandable.dart';
|
||||||
import 'package:flowy_sdk/log.dart';
|
import 'package:flowy_sdk/log.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
|
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';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
import 'package:app_flowy/core/folder_notification.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:app_flowy/core/notification_helper.dart';
|
|
||||||
import 'package:flowy_sdk/log.dart';
|
import 'package:flowy_sdk/log.dart';
|
||||||
import 'package:flowy_sdk/protobuf/dart-notify/subject.pb.dart';
|
import 'package:flowy_sdk/protobuf/dart-notify/subject.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-data-model/app.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
|
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),
|
||||||
|
@ -3,8 +3,8 @@ import 'dart:async';
|
|||||||
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-error/errors.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||||
|
|
||||||
import 'package:app_flowy/plugin/plugin.dart';
|
import 'package:app_flowy/plugin/plugin.dart';
|
||||||
|
|
||||||
@ -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
|
||||||
|
@ -4,12 +4,12 @@ import 'package:app_flowy/user/application/user_settings_service.dart';
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flowy_infra/theme.dart';
|
import 'package:flowy_infra/theme.dart';
|
||||||
import 'package:flowy_sdk/log.dart';
|
import 'package:flowy_sdk/log.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-user-data-model/user_setting.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-user/user_setting.pb.dart';
|
||||||
import 'package:flutter/material.dart';
|
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;
|
||||||
|
@ -2,9 +2,9 @@ import 'dart:convert';
|
|||||||
import 'package:app_flowy/workspace/application/doc/doc_service.dart';
|
import 'package:app_flowy/workspace/application/doc/doc_service.dart';
|
||||||
import 'package:app_flowy/workspace/application/trash/trash_service.dart';
|
import 'package:app_flowy/workspace/application/trash/trash_service.dart';
|
||||||
import 'package:app_flowy/workspace/application/view/view_listener.dart';
|
import 'package:app_flowy/workspace/application/view/view_listener.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/trash.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder/trash.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-data-model/view.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:flutter_quill/flutter_quill.dart' show Document, Delta;
|
import 'package:flutter_quill/flutter_quill.dart' show Document, Delta;
|
||||||
import 'package:flowy_sdk/log.dart';
|
import 'package:flowy_sdk/log.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
@ -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;
|
||||||
|
@ -1,29 +1,29 @@
|
|||||||
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-folder-data-model/view.pb.dart';
|
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';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-sync/text_block_info.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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:app_flowy/startup/tasks/rust_sdk.dart';
|
||||||
import 'package:app_flowy/workspace/application/doc/share_service.dart';
|
import 'package:app_flowy/workspace/application/doc/share_service.dart';
|
||||||
import 'package:app_flowy/workspace/application/markdown/delta_markdown.dart';
|
import 'package:app_flowy/workspace/application/markdown/delta_markdown.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-text-block/entities.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-text-block/entities.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
|
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';
|
||||||
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';
|
||||||
@ -10,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(
|
||||||
@ -30,11 +33,33 @@ 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);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Directory> get _exportDir async {
|
||||||
|
Directory documentsDir = await appFlowyDocumentDirectory();
|
||||||
|
|
||||||
|
return documentsDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> get _localPath async {
|
||||||
|
final dir = await _exportDir;
|
||||||
|
return dir.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<File> get _localFile async {
|
||||||
|
final path = await _localPath;
|
||||||
|
return File('$path/${view.name}.md');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<File> writeFile(String md) async {
|
||||||
|
final file = await _localFile;
|
||||||
|
return file.writeAsString(md);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
@ -48,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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'package:app_flowy/workspace/application/grid/grid_service.dart';
|
||||||
|
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
|
||||||
|
import 'package:flowy_sdk/log.dart';
|
||||||
|
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||||
|
|
||||||
|
import 'block_listener.dart';
|
||||||
|
|
||||||
|
/// Read https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/frontend/grid for more information
|
||||||
|
class GridBlockCache {
|
||||||
|
final String gridId;
|
||||||
|
final GridBlockPB block;
|
||||||
|
late GridRowCache _rowCache;
|
||||||
|
late GridBlockListener _listener;
|
||||||
|
|
||||||
|
List<GridRowInfo> get rows => _rowCache.rows;
|
||||||
|
GridRowCache get rowCache => _rowCache;
|
||||||
|
|
||||||
|
GridBlockCache({
|
||||||
|
required this.gridId,
|
||||||
|
required this.block,
|
||||||
|
required GridFieldCache fieldCache,
|
||||||
|
}) {
|
||||||
|
_rowCache = GridRowCache(
|
||||||
|
gridId: gridId,
|
||||||
|
block: block,
|
||||||
|
notifier: GridRowCacheFieldNotifierImpl(fieldCache),
|
||||||
|
);
|
||||||
|
|
||||||
|
_listener = GridBlockListener(blockId: block.id);
|
||||||
|
_listener.start((result) {
|
||||||
|
result.fold(
|
||||||
|
(changesets) => _rowCache.applyChangesets(changesets),
|
||||||
|
(err) => Log.error(err),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> dispose() async {
|
||||||
|
await _listener.stop();
|
||||||
|
await _rowCache.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void addListener({
|
||||||
|
required void Function(GridRowChangeReason) onChangeReason,
|
||||||
|
bool Function()? listenWhen,
|
||||||
|
}) {
|
||||||
|
_rowCache.onRowsChanged((reason) {
|
||||||
|
if (listenWhen != null && listenWhen() == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeReason(reason);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:app_flowy/core/grid_notification.dart';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:flowy_infra/notifier.dart';
|
||||||
|
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||||
|
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||||
|
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
|
||||||
|
|
||||||
|
typedef GridBlockUpdateNotifierValue = Either<List<GridBlockChangesetPB>, FlowyError>;
|
||||||
|
|
||||||
|
class GridBlockListener {
|
||||||
|
final String blockId;
|
||||||
|
PublishNotifier<GridBlockUpdateNotifierValue>? _rowsUpdateNotifier = PublishNotifier();
|
||||||
|
GridNotificationListener? _listener;
|
||||||
|
|
||||||
|
GridBlockListener({required this.blockId});
|
||||||
|
|
||||||
|
void start(void Function(GridBlockUpdateNotifierValue) onBlockChanged) {
|
||||||
|
if (_listener != null) {
|
||||||
|
_listener?.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
_listener = GridNotificationListener(
|
||||||
|
objectId: blockId,
|
||||||
|
handler: _handler,
|
||||||
|
);
|
||||||
|
|
||||||
|
_rowsUpdateNotifier?.addPublishListener(onBlockChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handler(GridNotification ty, Either<Uint8List, FlowyError> result) {
|
||||||
|
switch (ty) {
|
||||||
|
case GridNotification.DidUpdateGridBlock:
|
||||||
|
result.fold(
|
||||||
|
(payload) => _rowsUpdateNotifier?.value = left([GridBlockChangesetPB.fromBuffer(payload)]),
|
||||||
|
(error) => _rowsUpdateNotifier?.value = right(error),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> stop() async {
|
||||||
|
await _listener?.stop();
|
||||||
|
_rowsUpdateNotifier?.dispose();
|
||||||
|
_rowsUpdateNotifier = null;
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,10 @@
|
|||||||
|
import 'package:app_flowy/core/grid_notification.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.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/dart_notification.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
|
||||||
import 'package:flowy_infra/notifier.dart';
|
import 'package:flowy_infra/notifier.dart';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:app_flowy/core/notification_helper.dart';
|
|
||||||
|
|
||||||
typedef UpdateFieldNotifiedValue = Either<Unit, FlowyError>;
|
typedef UpdateFieldNotifiedValue = Either<Unit, FlowyError>;
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
part of 'cell_service.dart';
|
||||||
|
|
||||||
|
abstract class IGridCellDataConfig {
|
||||||
|
// The cell data will reload if it receives the field's change notification.
|
||||||
|
bool get reloadOnFieldChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class IGridCellDataParser<T> {
|
||||||
|
T? parserData(List<int> data);
|
||||||
|
}
|
||||||
|
|
||||||
|
class GridCellDataLoader<T> {
|
||||||
|
final CellService service = CellService();
|
||||||
|
final GridCellIdentifier cellId;
|
||||||
|
final IGridCellDataParser<T> parser;
|
||||||
|
final bool reloadOnFieldChanged;
|
||||||
|
|
||||||
|
GridCellDataLoader({
|
||||||
|
required this.cellId,
|
||||||
|
required this.parser,
|
||||||
|
this.reloadOnFieldChanged = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<T?> loadData() {
|
||||||
|
final fut = service.getCell(cellId: cellId);
|
||||||
|
return fut.then(
|
||||||
|
(result) => result.fold((GridCellPB cell) {
|
||||||
|
try {
|
||||||
|
return parser.parserData(cell.data);
|
||||||
|
} catch (e, s) {
|
||||||
|
Log.error('$parser parser cellData failed, $e');
|
||||||
|
Log.error('Stack trace \n $s');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, (err) {
|
||||||
|
Log.error(err);
|
||||||
|
return null;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StringCellDataParser implements IGridCellDataParser<String> {
|
||||||
|
@override
|
||||||
|
String? parserData(List<int> data) {
|
||||||
|
final s = utf8.decode(data);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DateCellDataParser implements IGridCellDataParser<DateCellDataPB> {
|
||||||
|
@override
|
||||||
|
DateCellDataPB? parserData(List<int> data) {
|
||||||
|
if (data.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return DateCellDataPB.fromBuffer(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SelectOptionCellDataParser implements IGridCellDataParser<SelectOptionCellDataPB> {
|
||||||
|
@override
|
||||||
|
SelectOptionCellDataPB? parserData(List<int> data) {
|
||||||
|
if (data.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return SelectOptionCellDataPB.fromBuffer(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class URLCellDataParser implements IGridCellDataParser<URLCellDataPB> {
|
||||||
|
@override
|
||||||
|
URLCellDataPB? parserData(List<int> data) {
|
||||||
|
if (data.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return URLCellDataPB.fromBuffer(data);
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -1,27 +1,30 @@
|
|||||||
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-data-model/grid.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/selection_type_option.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
|
||||||
|
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option_entities.pb.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package: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 'data_loader.dart';
|
part 'cell_data_loader.dart';
|
||||||
part 'context_builder.dart';
|
part 'context_builder.dart';
|
||||||
part 'data_cache.dart';
|
part 'cell_cache.dart';
|
||||||
part 'data_persistence.dart';
|
part 'cell_data_persistence.dart';
|
||||||
|
|
||||||
// key: rowId
|
// key: rowId
|
||||||
|
|
||||||
@ -29,45 +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,
|
||||||
Cell? cell,
|
}) = _GridCellIdentifier;
|
||||||
}) = _GridCell;
|
|
||||||
|
|
||||||
// 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}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 GridCellIdentifier _cellId;
|
||||||
final GridCellCache _cellCache;
|
final GridCellCache _cellCache;
|
||||||
final GridCell _gridCell;
|
final GridFieldCache _fieldCache;
|
||||||
GridCellContextBuilder({
|
|
||||||
required GridCellCache cellCache,
|
|
||||||
required GridCell gridCell,
|
|
||||||
}) : _cellCache = cellCache,
|
|
||||||
_gridCell = gridCell;
|
|
||||||
|
|
||||||
_GridCellContext build() {
|
GridCellControllerBuilder({
|
||||||
switch (_gridCell.field.fieldType) {
|
required GridCellIdentifier cellId,
|
||||||
|
required GridCellCache cellCache,
|
||||||
|
required GridFieldCache fieldCache,
|
||||||
|
}) : _cellCache = cellCache,
|
||||||
|
_fieldCache = fieldCache,
|
||||||
|
_cellId = cellId;
|
||||||
|
|
||||||
|
IGridCellController build() {
|
||||||
|
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 GridCellCache 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(objectId: 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(GridCellCacheData(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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,111 +0,0 @@
|
|||||||
part of 'cell_service.dart';
|
|
||||||
|
|
||||||
typedef GridCellMap = LinkedHashMap<String, GridCell>;
|
|
||||||
|
|
||||||
class GridCellCacheData {
|
|
||||||
GridCellCacheKey key;
|
|
||||||
dynamic object;
|
|
||||||
GridCellCacheData({
|
|
||||||
required this.key,
|
|
||||||
required this.object,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class GridCellCacheKey {
|
|
||||||
final String fieldId;
|
|
||||||
final String objectId;
|
|
||||||
GridCellCacheKey({
|
|
||||||
required this.fieldId,
|
|
||||||
required this.objectId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class GridCellFieldDelegate {
|
|
||||||
void onFieldChanged(void Function(String) callback);
|
|
||||||
void dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
class GridCellCache {
|
|
||||||
final String gridId;
|
|
||||||
final GridCellFieldDelegate fieldDelegate;
|
|
||||||
|
|
||||||
/// fieldId: {objectId: callback}
|
|
||||||
final Map<String, Map<String, List<VoidCallback>>> _fieldListenerByFieldId = {};
|
|
||||||
|
|
||||||
/// fieldId: {cacheKey: cacheData}
|
|
||||||
final Map<String, Map<String, dynamic>> _cellDataByFieldId = {};
|
|
||||||
GridCellCache({
|
|
||||||
required this.gridId,
|
|
||||||
required this.fieldDelegate,
|
|
||||||
}) {
|
|
||||||
fieldDelegate.onFieldChanged((fieldId) {
|
|
||||||
_cellDataByFieldId.remove(fieldId);
|
|
||||||
final map = _fieldListenerByFieldId[fieldId];
|
|
||||||
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.objectId] = [onFieldChanged];
|
|
||||||
} else {
|
|
||||||
var objects = map[cacheKey.objectId];
|
|
||||||
if (objects == null) {
|
|
||||||
map[cacheKey.objectId] = [onFieldChanged];
|
|
||||||
} else {
|
|
||||||
objects.add(onFieldChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeFieldListener(GridCellCacheKey cacheKey, VoidCallback fn) {
|
|
||||||
var callbacks = _fieldListenerByFieldId[cacheKey.fieldId]?[cacheKey.objectId];
|
|
||||||
final index = callbacks?.indexWhere((callback) => callback == fn);
|
|
||||||
if (index != null && index != -1) {
|
|
||||||
callbacks?.removeAt(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void insert<T extends GridCellCacheData>(T item) {
|
|
||||||
var map = _cellDataByFieldId[item.key.fieldId];
|
|
||||||
if (map == null) {
|
|
||||||
_cellDataByFieldId[item.key.fieldId] = {};
|
|
||||||
map = _cellDataByFieldId[item.key.fieldId];
|
|
||||||
}
|
|
||||||
|
|
||||||
map![item.key.objectId] = item.object;
|
|
||||||
}
|
|
||||||
|
|
||||||
T? get<T>(GridCellCacheKey key) {
|
|
||||||
final map = _cellDataByFieldId[key.fieldId];
|
|
||||||
if (map == null) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
final object = map[key.objectId];
|
|
||||||
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();
|
|
||||||
fieldDelegate.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,134 +0,0 @@
|
|||||||
part of 'cell_service.dart';
|
|
||||||
|
|
||||||
abstract class IGridCellDataConfig {
|
|
||||||
// The cell data will reload if it receives the field's change notification.
|
|
||||||
bool get reloadOnFieldChanged;
|
|
||||||
|
|
||||||
// When the reloadOnCellChanged is true, it will load the cell data after user input.
|
|
||||||
// For example: The number cell reload the cell data that carries the format
|
|
||||||
// user input: 12
|
|
||||||
// cell display: $12
|
|
||||||
bool get reloadOnCellChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
class GridCellDataConfig implements IGridCellDataConfig {
|
|
||||||
@override
|
|
||||||
final bool reloadOnCellChanged;
|
|
||||||
|
|
||||||
@override
|
|
||||||
final bool reloadOnFieldChanged;
|
|
||||||
|
|
||||||
const GridCellDataConfig({
|
|
||||||
this.reloadOnCellChanged = false,
|
|
||||||
this.reloadOnFieldChanged = false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class IGridCellDataLoader<T> {
|
|
||||||
Future<T?> loadData();
|
|
||||||
|
|
||||||
IGridCellDataConfig get config;
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class ICellDataParser<T> {
|
|
||||||
T? parserData(List<int> data);
|
|
||||||
}
|
|
||||||
|
|
||||||
class GridCellDataLoader<T> extends IGridCellDataLoader<T> {
|
|
||||||
final CellService service = CellService();
|
|
||||||
final GridCell gridCell;
|
|
||||||
final ICellDataParser<T> parser;
|
|
||||||
|
|
||||||
@override
|
|
||||||
final IGridCellDataConfig config;
|
|
||||||
|
|
||||||
GridCellDataLoader({
|
|
||||||
required this.gridCell,
|
|
||||||
required this.parser,
|
|
||||||
this.config = const GridCellDataConfig(),
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<T?> loadData() {
|
|
||||||
final fut = service.getCell(
|
|
||||||
gridId: gridCell.gridId,
|
|
||||||
fieldId: gridCell.field.id,
|
|
||||||
rowId: gridCell.rowId,
|
|
||||||
);
|
|
||||||
return fut.then(
|
|
||||||
(result) => result.fold((Cell cell) {
|
|
||||||
try {
|
|
||||||
return parser.parserData(cell.data);
|
|
||||||
} catch (e, s) {
|
|
||||||
Log.error('$parser parser cellData failed, $e');
|
|
||||||
Log.error('Stack trace \n $s');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}, (err) {
|
|
||||||
Log.error(err);
|
|
||||||
return null;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SelectOptionCellDataLoader extends IGridCellDataLoader<SelectOptionCellData> {
|
|
||||||
final SelectOptionService service;
|
|
||||||
final GridCell gridCell;
|
|
||||||
SelectOptionCellDataLoader({
|
|
||||||
required this.gridCell,
|
|
||||||
}) : service = SelectOptionService(gridCell: gridCell);
|
|
||||||
@override
|
|
||||||
Future<SelectOptionCellData?> loadData() async {
|
|
||||||
return service.getOpitonContext().then((result) {
|
|
||||||
return result.fold(
|
|
||||||
(data) => data,
|
|
||||||
(err) {
|
|
||||||
Log.error(err);
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
IGridCellDataConfig get config => const GridCellDataConfig(reloadOnFieldChanged: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
class StringCellDataParser implements ICellDataParser<String> {
|
|
||||||
@override
|
|
||||||
String? parserData(List<int> data) {
|
|
||||||
final s = utf8.decode(data);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DateCellDataParser implements ICellDataParser<DateCellData> {
|
|
||||||
@override
|
|
||||||
DateCellData? parserData(List<int> data) {
|
|
||||||
if (data.isEmpty) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return DateCellData.fromBuffer(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SelectOptionCellDataParser implements ICellDataParser<SelectOptionCellData> {
|
|
||||||
@override
|
|
||||||
SelectOptionCellData? parserData(List<int> data) {
|
|
||||||
if (data.isEmpty) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return SelectOptionCellData.fromBuffer(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class URLCellDataParser implements ICellDataParser<URLCellData> {
|
|
||||||
@override
|
|
||||||
URLCellData? parserData(List<int> data) {
|
|
||||||
if (data.isEmpty) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return URLCellData.fromBuffer(data);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
@ -225,14 +226,14 @@ class DateCalState with _$DateCalState {
|
|||||||
String _timeHintText(DateTypeOption typeOption) {
|
String _timeHintText(DateTypeOption typeOption) {
|
||||||
switch (typeOption.timeFormat) {
|
switch (typeOption.timeFormat) {
|
||||||
case TimeFormat.TwelveHour:
|
case TimeFormat.TwelveHour:
|
||||||
return LocaleKeys.grid_date_timeHintTextInTwelveHour.tr();
|
return LocaleKeys.document_date_timeHintTextInTwelveHour.tr();
|
||||||
case TimeFormat.TwentyFourHour:
|
case TimeFormat.TwentyFourHour:
|
||||||
return LocaleKeys.grid_date_timeHintTextInTwentyFourHour.tr();
|
return LocaleKeys.document_date_timeHintTextInTwentyFourHour.tr();
|
||||||
}
|
}
|
||||||
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;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Field;
|
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.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';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
@ -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;
|
||||||
|
@ -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),
|
||||||
|
@ -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(
|
||||||
|
@ -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({
|
||||||
|
@ -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;
|
||||||
|
@ -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() ?? "",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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 ?? "",
|
||||||
|
@ -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 ?? "",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:flowy_sdk/log.dart';
|
import 'package:flowy_sdk/log.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.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';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
@ -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,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:app_flowy/workspace/application/grid/field/field_listener.dart';
|
import 'package:app_flowy/workspace/application/grid/field/field_listener.dart';
|
||||||
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-data-model/grid.pb.dart' show Field;
|
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';
|
||||||
@ -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;
|
||||||
|
|
||||||
|
@ -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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.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,
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,13 +1,13 @@
|
|||||||
|
import 'package:app_flowy/core/grid_notification.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.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/dart_notification.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
|
||||||
import 'package:flowy_infra/notifier.dart';
|
import 'package:flowy_infra/notifier.dart';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:app_flowy/core/notification_helper.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;
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
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';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.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/grid_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: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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
);
|
||||||
|
}
|
@ -1,13 +1,13 @@
|
|||||||
|
import 'package:app_flowy/core/grid_notification.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.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/dart_notification.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
|
||||||
import 'package:flowy_infra/notifier.dart';
|
import 'package:flowy_infra/notifier.dart';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:app_flowy/core/notification_helper.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;
|
||||||
|
@ -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);
|
||||||
|
@ -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(),
|
||||||
);
|
);
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -86,7 +86,7 @@ extension NumberFormatExtension on NumberFormat {
|
|||||||
return "New Zealand dollar";
|
return "New Zealand dollar";
|
||||||
case NumberFormat.NorwegianKrone:
|
case NumberFormat.NorwegianKrone:
|
||||||
return "Norwegian krone";
|
return "Norwegian krone";
|
||||||
case NumberFormat.Number:
|
case NumberFormat.Num:
|
||||||
return "Number";
|
return "Number";
|
||||||
case NumberFormat.Percent:
|
case NumberFormat.Percent:
|
||||||
return "Percent";
|
return "Percent";
|
||||||
|
@ -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(),
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,9 @@ import 'package:app_flowy/workspace/application/grid/field/field_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';
|
||||||
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-data-model/grid.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 {
|
||||||
@ -19,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;
|
||||||
|
|
||||||
@ -34,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,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,
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:equatable/equatable.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-error/errors.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/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 'cell/cell_service/cell_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';
|
||||||
@ -14,25 +15,27 @@ import 'dart:collection';
|
|||||||
part 'grid_bloc.freezed.dart';
|
part 'grid_bloc.freezed.dart';
|
||||||
|
|
||||||
class GridBloc extends Bloc<GridEvent, GridState> {
|
class GridBloc extends Bloc<GridEvent, GridState> {
|
||||||
|
final String gridId;
|
||||||
final GridService _gridService;
|
final GridService _gridService;
|
||||||
final GridFieldCache fieldCache;
|
final GridFieldCache fieldCache;
|
||||||
late final GridRowCache rowCache;
|
|
||||||
late final GridCellCache cellCache;
|
|
||||||
|
|
||||||
GridBloc({required View view})
|
// key: the block id
|
||||||
: _gridService = GridService(gridId: view.id),
|
final LinkedHashMap<String, GridBlockCache> _blocks;
|
||||||
|
|
||||||
|
List<GridRowInfo> get rowInfos {
|
||||||
|
final List<GridRowInfo> rows = [];
|
||||||
|
for (var block in _blocks.values) {
|
||||||
|
rows.addAll(block.rows);
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
GridBloc({required ViewPB view})
|
||||||
|
: gridId = view.id,
|
||||||
|
_blocks = LinkedHashMap.identity(),
|
||||||
|
_gridService = GridService(gridId: view.id),
|
||||||
fieldCache = GridFieldCache(gridId: view.id),
|
fieldCache = GridFieldCache(gridId: view.id),
|
||||||
super(GridState.initial(view.id)) {
|
super(GridState.initial(view.id)) {
|
||||||
rowCache = GridRowCache(
|
|
||||||
gridId: view.id,
|
|
||||||
fieldDelegate: GridRowCacheDelegateImpl(fieldCache),
|
|
||||||
);
|
|
||||||
|
|
||||||
cellCache = GridCellCache(
|
|
||||||
gridId: view.id,
|
|
||||||
fieldDelegate: GridCellCacheDelegateImpl(fieldCache),
|
|
||||||
);
|
|
||||||
|
|
||||||
on<GridEvent>(
|
on<GridEvent>(
|
||||||
(event, emit) async {
|
(event, emit) async {
|
||||||
await event.when(
|
await event.when(
|
||||||
@ -43,11 +46,11 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
|||||||
createRow: () {
|
createRow: () {
|
||||||
_gridService.createRow();
|
_gridService.createRow();
|
||||||
},
|
},
|
||||||
didReceiveRowUpdate: (rows, listState) {
|
didReceiveRowUpdate: (newRowInfos, reason) {
|
||||||
emit(state.copyWith(rows: rows, listState: listState));
|
emit(state.copyWith(rowInfos: newRowInfos, reason: reason));
|
||||||
},
|
},
|
||||||
didReceiveFieldUpdate: (fields) {
|
didReceiveFieldUpdate: (fields) {
|
||||||
emit(state.copyWith(rows: rowCache.clonedRows, fields: GridFieldEquatable(fields)));
|
emit(state.copyWith(rowInfos: rowInfos, fields: GridFieldEquatable(fields)));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -57,21 +60,23 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
|||||||
@override
|
@override
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
await _gridService.closeGrid();
|
await _gridService.closeGrid();
|
||||||
await cellCache.dispose();
|
|
||||||
await rowCache.dispose();
|
|
||||||
await fieldCache.dispose();
|
await fieldCache.dispose();
|
||||||
|
|
||||||
|
for (final blockCache in _blocks.values) {
|
||||||
|
blockCache.dispose();
|
||||||
|
}
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GridRowCache? getRowCache(String blockId, String rowId) {
|
||||||
|
final GridBlockCache? blockCache = _blocks[blockId];
|
||||||
|
return blockCache?.rowCache;
|
||||||
|
}
|
||||||
|
|
||||||
void _startListening() {
|
void _startListening() {
|
||||||
fieldCache.addListener(
|
fieldCache.addListener(
|
||||||
listenWhen: () => !isClosed,
|
listenWhen: () => !isClosed,
|
||||||
onChanged: (fields) => add(GridEvent.didReceiveFieldUpdate(fields)),
|
onFields: (fields) => add(GridEvent.didReceiveFieldUpdate(fields)),
|
||||||
);
|
|
||||||
|
|
||||||
rowCache.addListener(
|
|
||||||
listenWhen: () => !isClosed,
|
|
||||||
onChanged: (rows, listState) => add(GridEvent.didReceiveRowUpdate(rowCache.clonedRows, listState)),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,24 +84,26 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
|||||||
final result = await _gridService.loadGrid();
|
final result = await _gridService.loadGrid();
|
||||||
return Future(
|
return Future(
|
||||||
() => result.fold(
|
() => result.fold(
|
||||||
(grid) async => await _loadFields(grid, emit),
|
(grid) async {
|
||||||
|
_initialBlocks(grid.blocks);
|
||||||
|
await _loadFields(grid, emit);
|
||||||
|
},
|
||||||
(err) => emit(state.copyWith(loadingState: GridLoadingState.finish(right(err)))),
|
(err) => emit(state.copyWith(loadingState: GridLoadingState.finish(right(err)))),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
fieldCache.fields = fields.items;
|
fieldCache.fields = fields.items;
|
||||||
rowCache.resetRows(grid.blockOrders);
|
|
||||||
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
grid: Some(grid),
|
grid: Some(grid),
|
||||||
fields: GridFieldEquatable(fieldCache.fields),
|
fields: GridFieldEquatable(fieldCache.fields),
|
||||||
rows: rowCache.clonedRows,
|
rowInfos: rowInfos,
|
||||||
loadingState: GridLoadingState.finish(left(unit)),
|
loadingState: GridLoadingState.finish(left(unit)),
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
@ -104,34 +111,57 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _initialBlocks(List<GridBlockPB> blocks) {
|
||||||
|
for (final block in blocks) {
|
||||||
|
if (_blocks[block.id] != null) {
|
||||||
|
Log.warn("Intial duplicate block's cache: ${block.id}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final cache = GridBlockCache(
|
||||||
|
gridId: gridId,
|
||||||
|
block: block,
|
||||||
|
fieldCache: fieldCache,
|
||||||
|
);
|
||||||
|
|
||||||
|
cache.addListener(
|
||||||
|
listenWhen: () => !isClosed,
|
||||||
|
onChangeReason: (reason) => add(GridEvent.didReceiveRowUpdate(rowInfos, reason)),
|
||||||
|
);
|
||||||
|
|
||||||
|
_blocks[block.id] = cache;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
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 listState,
|
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(),
|
||||||
listState: const InitialListState(),
|
reason: const InitialListState(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,9 +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<GridFieldPB> fields) : _fields = fields;
|
||||||
const GridFieldEquatable(List<Field> fields) : _fields = fields;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props {
|
List<Object?> get props {
|
||||||
@ -154,5 +183,5 @@ class GridFieldEquatable extends Equatable {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
UnmodifiableListView<Field> get value => UnmodifiableListView(_fields);
|
UnmodifiableListView<GridFieldPB> get value => UnmodifiableListView(_fields);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
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-data-model/grid.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';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
@ -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));
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
|
|||||||
|
|
||||||
Future<void> _startListening() async {
|
Future<void> _startListening() async {
|
||||||
fieldCache.addListener(
|
fieldCache.addListener(
|
||||||
onChanged: (fields) => add(GridHeaderEvent.didReceiveFieldUpdate(fields)),
|
onFields: (fields) => add(GridHeaderEvent.didReceiveFieldUpdate(fields)),
|
||||||
listenWhen: () => !isClosed,
|
listenWhen: () => !isClosed,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
import 'package:dartz/dartz.dart';
|
|
||||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
|
|
||||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
|
||||||
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
|
|
||||||
import 'package:flowy_infra/notifier.dart';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'package:app_flowy/core/notification_helper.dart';
|
|
||||||
|
|
||||||
class GridRowListener {
|
|
||||||
final String gridId;
|
|
||||||
PublishNotifier<Either<List<GridRowsChangeset>, FlowyError>> rowsUpdateNotifier = PublishNotifier(comparable: null);
|
|
||||||
GridNotificationListener? _listener;
|
|
||||||
|
|
||||||
GridRowListener({required this.gridId});
|
|
||||||
|
|
||||||
void start() {
|
|
||||||
_listener = GridNotificationListener(
|
|
||||||
objectId: gridId,
|
|
||||||
handler: _handler,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handler(GridNotification ty, Either<Uint8List, FlowyError> result) {
|
|
||||||
switch (ty) {
|
|
||||||
case GridNotification.DidUpdateGridRow:
|
|
||||||
result.fold(
|
|
||||||
(payload) => rowsUpdateNotifier.value = left([GridRowsChangeset.fromBuffer(payload)]),
|
|
||||||
(error) => rowsUpdateNotifier.value = right(error),
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> stop() async {
|
|
||||||
await _listener?.stop();
|
|
||||||
rowsUpdateNotifier.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,10 +5,12 @@ import 'package:dartz/dartz.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-folder-data-model/view.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||||
|
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||||
|
import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
|
||||||
|
import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'cell/cell_service/cell_service.dart';
|
|
||||||
import 'row/row_service.dart';
|
import 'row/row_service.dart';
|
||||||
|
|
||||||
class GridService {
|
class GridService {
|
||||||
@ -17,60 +19,61 @@ 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 GridEventGetGridData(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 ChangesetListener = void Function(GridFieldChangeset);
|
typedef FieldChangesetCallback = void Function(GridFieldChangesetPB);
|
||||||
|
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 List<ChangesetListener> _changesetListener = [];
|
final Map<FieldsCallback, VoidCallback> _fieldsCallbackMap = {};
|
||||||
|
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) {
|
||||||
_deleteFields(changeset.deletedFields);
|
_deleteFields(changeset.deletedFields);
|
||||||
_insertFields(changeset.insertedFields);
|
_insertFields(changeset.insertedFields);
|
||||||
_updateFields(changeset.updatedFields);
|
_updateFields(changeset.updatedFields);
|
||||||
for (final listener in _changesetListener) {
|
for (final listener in _changesetCallbackMap.values) {
|
||||||
listener(changeset);
|
listener(changeset);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -85,55 +88,65 @@ 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];
|
||||||
}
|
}
|
||||||
|
|
||||||
VoidCallback addListener(
|
void addListener({
|
||||||
{VoidCallback? listener, void Function(List<Field>)? onChanged, bool Function()? listenWhen}) {
|
FieldsCallback? onFields,
|
||||||
f() {
|
FieldChangesetCallback? onChangeset,
|
||||||
if (listenWhen != null && listenWhen() == false) {
|
bool Function()? listenWhen,
|
||||||
return;
|
}) {
|
||||||
|
if (onChangeset != null) {
|
||||||
|
fn(c) {
|
||||||
|
if (listenWhen != null && listenWhen() == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onChangeset(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onChanged != null) {
|
_changesetCallbackMap[onChangeset] = fn;
|
||||||
onChanged(fields);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listener != null) {
|
|
||||||
listener();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_fieldNotifier?.addListener(f);
|
if (onFields != null) {
|
||||||
return f;
|
fn() {
|
||||||
}
|
if (listenWhen != null && listenWhen() == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onFields(fields);
|
||||||
|
}
|
||||||
|
|
||||||
void removeListener(VoidCallback f) {
|
_fieldsCallbackMap[onFields] = fn;
|
||||||
_fieldNotifier?.removeListener(f);
|
_fieldNotifier?.addListener(fn);
|
||||||
}
|
|
||||||
|
|
||||||
void addChangesetListener(ChangesetListener listener) {
|
|
||||||
_changesetListener.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeChangesetListener(ChangesetListener listener) {
|
|
||||||
final index = _changesetListener.indexWhere((element) => element == listener);
|
|
||||||
if (index != -1) {
|
|
||||||
_changesetListener.removeAt(index);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _deleteFields(List<FieldOrder> deletedFields) {
|
void removeListener({
|
||||||
|
FieldsCallback? onFieldsListener,
|
||||||
|
FieldChangesetCallback? onChangsetListener,
|
||||||
|
}) {
|
||||||
|
if (onFieldsListener != null) {
|
||||||
|
final fn = _fieldsCallbackMap.remove(onFieldsListener);
|
||||||
|
if (fn != null) {
|
||||||
|
_fieldNotifier?.removeListener(fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onChangsetListener != null) {
|
||||||
|
_changesetCallbackMap.remove(onChangsetListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -141,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);
|
||||||
@ -156,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) {
|
||||||
@ -172,43 +185,42 @@ class GridFieldCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GridRowCacheDelegateImpl extends GridRowFieldDelegate {
|
class GridRowCacheFieldNotifierImpl extends GridRowCacheFieldNotifier {
|
||||||
final GridFieldCache _cache;
|
final GridFieldCache _cache;
|
||||||
GridRowCacheDelegateImpl(GridFieldCache cache) : _cache = cache;
|
FieldChangesetCallback? _onChangesetFn;
|
||||||
|
FieldsCallback? _onFieldFn;
|
||||||
|
GridRowCacheFieldNotifierImpl(GridFieldCache cache) : _cache = cache;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
UnmodifiableListView<Field> get fields => _cache.unmodifiableFields;
|
UnmodifiableListView<GridFieldPB> get fields => _cache.unmodifiableFields;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onFieldChanged(FieldDidUpdateCallback callback) {
|
void onFieldsChanged(VoidCallback callback) {
|
||||||
_cache.addListener(listener: () {
|
_onFieldFn = (_) => callback();
|
||||||
callback();
|
_cache.addListener(onFields: _onFieldFn);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class GridCellCacheDelegateImpl extends GridCellFieldDelegate {
|
|
||||||
final GridFieldCache _cache;
|
|
||||||
ChangesetListener? _changesetFn;
|
|
||||||
GridCellCacheDelegateImpl(GridFieldCache cache) : _cache = cache;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onFieldChanged(void Function(String) callback) {
|
void onFieldChanged(void Function(GridFieldPB) callback) {
|
||||||
changesetFn(GridFieldChangeset changeset) {
|
_onChangesetFn = (GridFieldChangesetPB changeset) {
|
||||||
for (final updatedField in changeset.updatedFields) {
|
for (final updatedField in changeset.updatedFields) {
|
||||||
callback(updatedField.id);
|
callback(updatedField);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
_cache.addChangesetListener(changesetFn);
|
_cache.addListener(onChangeset: _onChangesetFn);
|
||||||
_changesetFn = changesetFn;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
if (_changesetFn != null) {
|
if (_onFieldFn != null) {
|
||||||
_cache.removeChangesetListener(_changesetFn!);
|
_cache.removeListener(onFieldsListener: _onFieldFn!);
|
||||||
_changesetFn = null;
|
_onFieldFn = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_onChangesetFn != null) {
|
||||||
|
_cache.removeListener(onChangsetListener: _onChangesetFn!);
|
||||||
|
_onChangesetFn = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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';
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user