Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba957c0e81 | ||
|
|
84200ddda4 | ||
|
|
60065ddfb2 | ||
|
|
e157c19174 | ||
|
|
781f0ab88b | ||
|
|
5004729b72 | ||
|
|
e5703f83fb | ||
|
|
e77fef3a19 | ||
|
|
5f760ad578 | ||
|
|
e2f6f68923 | ||
|
|
3491ffdd08 | ||
|
|
1df2619c9f | ||
|
|
4d5063de6a | ||
|
|
8c0b8a875c | ||
|
|
897adbe21c | ||
|
|
a41894a5ec | ||
|
|
cc9bd30356 | ||
|
|
addcabea44 | ||
|
|
4b605b6373 | ||
|
|
70e8b2e256 | ||
|
|
069519589e | ||
|
|
d09574951b | ||
|
|
c9166137be | ||
|
|
d505314ab1 | ||
|
|
2e91dfb4be | ||
|
|
71022ed934 | ||
|
|
aa15a45097 | ||
|
|
fb30989cf8 | ||
|
|
5b07656295 | ||
|
|
5de3912fe3 | ||
|
|
0d8adaa921 | ||
|
|
000569a836 | ||
|
|
3de4e1cb12 | ||
|
|
b34aac3f07 | ||
|
|
73ad789bf0 | ||
|
|
b2bc59c6a1 | ||
|
|
4fb2afe82e | ||
|
|
7a750e5255 | ||
|
|
baa5d0c59b | ||
|
|
243a781b6c | ||
|
|
347245aaa1 | ||
|
|
67f07463f0 | ||
|
|
d2db49efab | ||
|
|
0100305af0 | ||
|
|
128bf02ffb | ||
|
|
5d125091d9 | ||
|
|
9c44b30847 | ||
|
|
aeb29d2e42 | ||
|
|
115bf20ce0 | ||
|
|
d36aea648c | ||
|
|
3d56a0a843 | ||
|
|
1b654e556f | ||
|
|
d77c41013f | ||
|
|
e6b186cc43 | ||
|
|
ceb39a4fd9 | ||
|
|
f64346c955 | ||
|
|
ba653ff463 | ||
|
|
834152dffc | ||
|
|
707ea4c8c8 | ||
|
|
9215f5188c | ||
|
|
53118a6c51 | ||
|
|
6a36bcd31d | ||
|
|
860c5d100b | ||
|
|
fe4e28b576 | ||
|
|
b7ba189642 | ||
|
|
85bfb8ad80 | ||
|
|
d0fd68da95 | ||
|
|
92baa573e1 | ||
|
|
c46b09f182 | ||
|
|
ab108e1dc1 | ||
|
|
fa0a334d6c | ||
|
|
310236dca0 | ||
|
|
fc1efeb70b | ||
|
|
494e31993b | ||
|
|
34d4ea3e54 | ||
|
|
91efcafd77 | ||
|
|
e8b21955f8 | ||
|
|
e479425297 | ||
|
|
7572321813 | ||
|
|
37f269b08b | ||
|
|
a0b5f09b06 | ||
|
|
d4946f1194 | ||
|
|
7949d3fe4a | ||
|
|
e9f8796809 | ||
|
|
3ba3a8dc18 | ||
|
|
50f9ac1657 | ||
|
|
2fb0e8da28 | ||
|
|
b25db83bc0 | ||
|
|
c181f91175 | ||
|
|
e52f59b8c1 | ||
|
|
e08ab1fc10 | ||
|
|
d7410cd6e8 | ||
|
|
06c5f6a790 | ||
|
|
35a72f701b | ||
|
|
1a2af1cf49 | ||
|
|
340f27cf87 | ||
|
|
d8958e82c8 | ||
|
|
a07a180cc8 | ||
|
|
3eb1360324 | ||
|
|
e0a392db00 | ||
|
|
2006d35a50 | ||
|
|
515cd50ac4 | ||
|
|
c5b15daac4 | ||
|
|
ddc99d646c | ||
|
|
b8f9da6bc7 | ||
|
|
2c244203fb | ||
|
|
bfd2754bda | ||
|
|
2ccc2d7c98 | ||
|
|
52c09c863c | ||
|
|
c1880292ac | ||
|
|
0e88d643cf | ||
|
|
c932f63e57 | ||
|
|
bce175255f | ||
|
|
273aaeec75 | ||
|
|
17def1ffab | ||
|
|
c05c1e191c | ||
|
|
9cd83d02a9 | ||
|
|
35a93939fe | ||
|
|
eed6c753dd | ||
|
|
436291c01a | ||
|
|
a2b5d6fa99 | ||
|
|
5c1b084789 | ||
|
|
b09f37e025 | ||
|
|
aa5f052ecf | ||
|
|
aae8259f63 | ||
|
|
52c8f32c82 | ||
|
|
8139856d6d | ||
|
|
b83b18274f | ||
|
|
05f99ee4a4 | ||
|
|
e5717b834d | ||
|
|
faf8f73206 | ||
|
|
70689d3239 | ||
|
|
f5b7d3951f | ||
|
|
5a30f46b85 | ||
|
|
6a465ac3e7 | ||
|
|
e4b07e69fa | ||
|
|
f9cc05319b | ||
|
|
873a46e443 | ||
|
|
9a387b275f | ||
|
|
b6ce7aff9f | ||
|
|
eee3dcf43a | ||
|
|
384f8ddcb6 | ||
|
|
af716be7aa | ||
|
|
86e31253de | ||
|
|
3f00e78508 | ||
|
|
45c73a20c2 | ||
|
|
ce86227da3 | ||
|
|
4643851b3f | ||
|
|
5d7008edd7 | ||
|
|
079fab6118 | ||
|
|
b5f6591daa | ||
|
|
9f7f86a49a | ||
|
|
260705280f | ||
|
|
60d9a6b3ab | ||
|
|
67e350e797 | ||
|
|
85e489babb | ||
|
|
d0a914c10f | ||
|
|
bb312b0a79 | ||
|
|
8cdf6f9ec0 | ||
|
|
9666269e27 | ||
|
|
31458e817a | ||
|
|
9ba17e004e | ||
|
|
e141e7ee63 | ||
|
|
3932d0661d | ||
|
|
98d408af87 | ||
|
|
15e3d3b486 | ||
|
|
1ac1fabb6e | ||
|
|
6b4291ccc7 | ||
|
|
0cf0b29721 | ||
|
|
2a75ad144a | ||
|
|
d487820963 | ||
|
|
350c55e71d | ||
|
|
fd603fe80f | ||
|
|
07ebb0cb95 | ||
|
|
9dfc8dbb43 | ||
|
|
3f27576066 | ||
|
|
247a19b0c6 | ||
|
|
8494e0f0a6 | ||
|
|
e94eb20388 | ||
|
|
8bbae46a7c | ||
|
|
8521cc0897 | ||
|
|
12441f1183 | ||
|
|
53b7595de8 | ||
|
|
8c225fe547 | ||
|
|
a507fb8ec6 | ||
|
|
4db5b7a544 | ||
|
|
1de5b274c3 | ||
|
|
12b549bad2 | ||
|
|
6dbb08c2ae | ||
|
|
9711d670ca | ||
|
|
442dfe7ef8 | ||
|
|
f81d5eb23e | ||
|
|
32be754eb7 | ||
|
|
0643e93e94 | ||
|
|
681d9028e3 | ||
|
|
49574d3ff0 | ||
|
|
c64b83c2d6 | ||
|
|
52d65c4c4f | ||
|
|
4da786a439 | ||
|
|
114968f2ee | ||
|
|
29952bc7fd | ||
|
|
64ee3140a3 | ||
|
|
a2f9ca2f28 | ||
|
|
72dc0b8f67 | ||
|
|
721781f0ae | ||
|
|
0d879a6091 | ||
|
|
806a924fff | ||
|
|
9488f42749 | ||
|
|
a2161216d8 | ||
|
|
aca9b81c79 | ||
|
|
81dd353d12 | ||
|
|
eb1659fd3e | ||
|
|
c89277d507 | ||
|
|
fa01dbb572 | ||
|
|
63613689cd | ||
|
|
89f89e8822 | ||
|
|
a801fa627c | ||
|
|
c6c164d347 | ||
|
|
be4222a62f | ||
|
|
4fa2d6dc2e | ||
|
|
e476337a6a | ||
|
|
44f9254c0a | ||
|
|
89becbfe71 | ||
|
|
cf42d8cd43 | ||
|
|
2b27fe85aa | ||
|
|
30e7323cf7 | ||
|
|
39a97499de | ||
|
|
157f929ff9 | ||
|
|
bf36ef7fd9 | ||
|
|
7ba638268b | ||
|
|
96c427e869 | ||
|
|
fff4af53e2 | ||
|
|
6910004003 | ||
|
|
7cb964c7a2 | ||
|
|
eaa1cb5a67 | ||
|
|
553cfb3f5e | ||
|
|
086195d2cf | ||
|
|
64d9b76e3a | ||
|
|
5ce8087c91 | ||
|
|
6117f84373 | ||
|
|
c8b90974fb | ||
|
|
f1a25ab4b7 | ||
|
|
2d11d3a68d | ||
|
|
d89a89131e | ||
|
|
a800e01f0b | ||
|
|
29e07089ca | ||
|
|
3cdd6665b3 | ||
|
|
1ba299815e | ||
|
|
b4671c1d99 | ||
|
|
aa85970b0f | ||
|
|
3b3b61e67c | ||
|
|
f02e77fcd8 | ||
|
|
5adf3139ba | ||
|
|
c127fd28b8 | ||
|
|
d7783a6440 | ||
|
|
fba69767a7 | ||
|
|
acaaabee2a | ||
|
|
925ef48a83 | ||
|
|
80d1cbabe0 | ||
|
|
43031b50eb | ||
|
|
52ba0c0092 | ||
|
|
0e55cd07a4 | ||
|
|
14ac2db06d | ||
|
|
a162b02476 | ||
|
|
04e14c6bba | ||
|
|
d75645c4bc | ||
|
|
6d6e61956a | ||
|
|
37b119172b | ||
|
|
0ba26e0a84 |
No files matched your search
@@ -15,6 +15,7 @@ on:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
FLUTTER_VERSION: "3.3.9"
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
@@ -30,7 +31,7 @@ jobs:
|
||||
- uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: "3.0.5"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
- name: Run FlowyEditor tests
|
||||
@@ -44,6 +45,7 @@ jobs:
|
||||
- uses: codecov/codecov-action@v3
|
||||
with:
|
||||
name: appflowy_editor
|
||||
flags: appflowy_editor
|
||||
env_vars: ${{ matrix.os }}
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
|
||||
name: Flutter lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/app_flowy/**"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/app_flowy/**"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
flutter-analyze:
|
||||
name: flutter analyze
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- uses: subosito/flutter-action@v1
|
||||
with:
|
||||
flutter-version: "3.0.5"
|
||||
channel: "stable"
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: "stable-2022-04-07"
|
||||
|
||||
- name: Cache Cargo
|
||||
id: cache-cargo
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cargo
|
||||
key: ${{ runner.os }}-cargo-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
|
||||
|
||||
- name: Cache Rust
|
||||
id: cache-rust-target
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
frontend/rust-lib/target
|
||||
shared-lib/target
|
||||
key: ${{ runner.os }}-rust-rust-lib-share-lib-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
|
||||
|
||||
- if: steps.cache-cargo.outputs.cache-hit != 'true'
|
||||
name: Rust Deps
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo install cargo-make
|
||||
|
||||
- name: Cargo make flowy dev
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make appflowy-deps-tools
|
||||
|
||||
- name: Flutter Deps
|
||||
run: flutter packages pub get
|
||||
working-directory: frontend/app_flowy
|
||||
|
||||
- name: Build FlowySDK
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make --profile development-linux-x86_64 appflowy-sdk-dev
|
||||
|
||||
- name: Flutter Code Generation
|
||||
working-directory: frontend/app_flowy
|
||||
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 build_runner build --delete-conflicting-outputs
|
||||
|
||||
- name: Run Flutter Analyzer
|
||||
working-directory: frontend/app_flowy
|
||||
run: flutter analyze
|
||||
@@ -1,80 +0,0 @@
|
||||
name: Frontend test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/app_flowy/**"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/app_flowy/**"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: "stable-2022-04-07"
|
||||
|
||||
- uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: "3.0.5"
|
||||
cache: true
|
||||
|
||||
- name: Cache Cargo
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cargo
|
||||
key: ${{ runner.os }}-cargo-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
|
||||
|
||||
- name: Cache Rust
|
||||
id: cache-rust-target
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
frontend/rust-lib/target
|
||||
shared-lib/target
|
||||
key: ${{ runner.os }}-rust-rust-lib-share-lib-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
|
||||
|
||||
- if: steps.cache-cargo.outputs.cache-hit != 'true'
|
||||
name: Rust Deps
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo install cargo-make
|
||||
cargo make appflowy-deps-tools
|
||||
|
||||
- name: Flutter Deps
|
||||
working-directory: frontend/app_flowy
|
||||
run: |
|
||||
flutter config --enable-linux-desktop
|
||||
|
||||
- name: Build Test lib
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make --profile test-linux build-test-lib
|
||||
|
||||
- name: Flutter Code Generation
|
||||
working-directory: frontend/app_flowy
|
||||
run: |
|
||||
flutter packages pub get
|
||||
flutter packages pub run easy_localization:generate -f keys -o locale_keys.g.dart -S assets/translations -s en.json
|
||||
flutter packages pub run build_runner build --delete-conflicting-outputs
|
||||
|
||||
- name: Run bloc tests
|
||||
working-directory: frontend/app_flowy
|
||||
run: |
|
||||
flutter pub get
|
||||
flutter test
|
||||
@@ -1,89 +1,87 @@
|
||||
name: CI
|
||||
name: Flutter-CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/**"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/**"
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.3.9"
|
||||
RUST_TOOLCHAIN: "1.65"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
flutter_profile: development-linux-x86_64
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
flutter_profile: development-mac-x86_64
|
||||
target: x86_64-apple-darwin
|
||||
- os: windows-latest
|
||||
flutter_profile: development-windows-x86
|
||||
target: x86_64-pc-windows-msvc
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- id: rust_toolchain
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: "stable-2022-04-07"
|
||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- id: flutter
|
||||
- name: Install flutter
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
flutter-version: "3.0.5"
|
||||
|
||||
- name: Cache Cargo
|
||||
id: cache-cargo
|
||||
uses: actions/cache@v2
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cargo
|
||||
key: ${{ runner.os }}-cargo-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
|
||||
prefix-key: ${{ matrix.job.os }}
|
||||
|
||||
- name: Cache Rust
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
frontend/rust-lib/target
|
||||
shared-lib/target
|
||||
key: ${{ runner.os }}-rust-rust-lib-share-lib-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
|
||||
|
||||
- name: Setup Environment
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo install --force cargo-make
|
||||
cargo install --force duckscript_cli
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dart curl build-essential libsqlite3-dev libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
|
||||
sudo apt-get install keybinder-3.0
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
vcpkg integrate install
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
echo 'do nothing'
|
||||
fi
|
||||
cargo make appflowy-flutter-deps-tools
|
||||
shell: bash
|
||||
|
||||
- if: steps.cache-cargo.outputs.cache-hit != 'true'
|
||||
name: Deps
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo install cargo-make
|
||||
cargo install duckscript_cli
|
||||
|
||||
- name: Cargo make appflowy-deps-tools
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make appflowy-deps-tools
|
||||
|
||||
- name: Config Flutter
|
||||
- name: Enable Flutter Desktop
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
flutter config --enable-linux-desktop
|
||||
@@ -94,7 +92,16 @@ jobs:
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Build
|
||||
- name: Build AppFlowy
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make --profile ${{ matrix.flutter_profile }} appflowy-dev
|
||||
|
||||
- name: Flutter Analyzer
|
||||
working-directory: frontend/app_flowy
|
||||
run: flutter analyze
|
||||
|
||||
- name: Run Flutter unit tests
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make dart_unit_test
|
||||
@@ -0,0 +1,128 @@
|
||||
name: integration test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/app_flowy/**"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/app_flowy/**"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: "stable-2022-04-07"
|
||||
|
||||
- uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: "3.3.9"
|
||||
cache: true
|
||||
|
||||
- name: Cache Cargo
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cargo
|
||||
key: ${{ runner.os }}-cargo-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
|
||||
|
||||
- name: Cache Rust
|
||||
id: cache-rust-target
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
frontend/rust-lib/target
|
||||
shared-lib/target
|
||||
key: ${{ runner.os }}-rust-rust-lib-share-lib-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
|
||||
|
||||
- name: Setup Environment
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dart curl build-essential libsqlite3-dev libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
|
||||
sudo apt-get install keybinder-3.0
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
vcpkg integrate install
|
||||
cargo install --force duckscript_cli
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
echo 'do nothing'
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- if: steps.cache-cargo.outputs.cache-hit != 'true'
|
||||
name: Rust Deps
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo install cargo-make
|
||||
cargo make appflowy-flutter-deps-tools
|
||||
|
||||
- name: Build Test lib
|
||||
working-directory: frontend
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
cargo make --profile production-linux-x86_64 appflowy
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
cargo make --profile production-mac-x86_64 appflowy
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
cargo make --profile production-windows-x86 appflowy
|
||||
fi
|
||||
|
||||
- name: Config Flutter
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
flutter config --enable-linux-desktop
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
flutter config --enable-macos-desktop
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
flutter config --enable-windows-desktop
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Flutter Code Generation
|
||||
working-directory: frontend/app_flowy
|
||||
run: |
|
||||
flutter packages pub get
|
||||
flutter packages pub run easy_localization:generate -f keys -o locale_keys.g.dart -S assets/translations -s en.json
|
||||
flutter packages pub run build_runner build --delete-conflicting-outputs
|
||||
|
||||
- name: Run AppFlowy tests
|
||||
working-directory: frontend/app_flowy
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
flutter test integration_test -d Linux --coverage
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
flutter test integration_test -d macOS --coverage
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
flutter test integration_test -d Windows --coverage
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- uses: codecov/codecov-action@v3
|
||||
with:
|
||||
name: appflowy
|
||||
flags: appflowy
|
||||
env_vars: ${{ matrix.os }}
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
|
||||
+267
-194
@@ -5,6 +5,10 @@ on:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.3.9"
|
||||
RUST_TOOLCHAIN: "1.65"
|
||||
|
||||
jobs:
|
||||
create-release:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -31,211 +35,50 @@ jobs:
|
||||
release_name: v${{ github.ref }}
|
||||
body_path: ${{ env.RELEASE_NOTES_PATH }}
|
||||
|
||||
build-linux-x86:
|
||||
runs-on: ubuntu-latest
|
||||
needs: create-release
|
||||
env:
|
||||
LINUX_APP_RELEASE_PATH: frontend/app_flowy/product/${{ github.ref_name }}/linux/Release
|
||||
LINUX_ZIP_NAME: AppFlowy-linux-x86.tar.gz
|
||||
LINUX_PACKAGE_NAME: AppFlowy_${{ github.ref_name }}_linux-amd64.deb
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup environment - Rust and Cargo
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 'stable-2022-04-07'
|
||||
|
||||
- name: Setup environment - Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: 'stable'
|
||||
flutter-version: '3.0.5'
|
||||
|
||||
- name: Pre build
|
||||
working-directory: frontend
|
||||
run: |
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential libsqlite3-dev libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
|
||||
sudo apt-get install keybinder-3.0
|
||||
source $HOME/.cargo/env
|
||||
cargo install --force cargo-make
|
||||
cargo install --force duckscript_cli
|
||||
cargo make appflowy-deps-tools
|
||||
|
||||
- name: Build Linux app
|
||||
working-directory: frontend
|
||||
run: |
|
||||
flutter config --enable-linux-desktop
|
||||
cargo make --env APP_VERSION=${{ github.ref_name }} --profile production-linux-x86_64 appflowy
|
||||
|
||||
- name: Build Linux package
|
||||
working-directory: ${{ env.LINUX_APP_RELEASE_PATH }}
|
||||
run: |
|
||||
mkdir -p package/opt && mv AppFlowy package/opt/
|
||||
cd package && mkdir DEBIAN
|
||||
# Create control file
|
||||
printf 'Package: AppFlowy
|
||||
Version: %s
|
||||
Architecture: amd64
|
||||
Essential: no
|
||||
Priority: optional
|
||||
Maintainer: AppFlowy
|
||||
Description: An Open Source Alternative to Notion\n' "${{ github.ref_name }}" > DEBIAN/control
|
||||
|
||||
# postinst script for creating symlink
|
||||
printf '#!/bin/bash
|
||||
if [ -e /usr/local/bin/appflowy ]; then
|
||||
echo "Symlink already exists, skipping."
|
||||
else
|
||||
echo "Creating Symlink in /usr/local/bin/appflowy"
|
||||
ln -s /opt/AppFlowy/app_flowy /usr/local/bin/appflowy
|
||||
fi' > DEBIAN/postinst
|
||||
chmod 0755 DEBIAN/postinst
|
||||
|
||||
# postrm script for cleaning up residuals
|
||||
printf '#!/bin/bash
|
||||
if [ -e /usr/local/bin/appflowy ]; then
|
||||
rm /usr/local/bin/appflowy
|
||||
fi' > DEBIAN/postrm
|
||||
chmod 0755 DEBIAN/postrm
|
||||
|
||||
mkdir -p usr/share/applications
|
||||
# Update Exec & icon path in desktop entry
|
||||
grep -rl "\[CHANGE_THIS\]" ./opt/AppFlowy/appflowy.desktop.temp | xargs sed -i "s/\[CHANGE_THIS\]/\/opt/"
|
||||
# Add desktop entry in package
|
||||
mv ./opt/AppFlowy/appflowy.desktop.temp ./usr/share/applications/appflowy.desktop
|
||||
|
||||
# Build
|
||||
cd ../ && dpkg-deb --build --root-owner-group package ${{ env.LINUX_PACKAGE_NAME }}
|
||||
|
||||
- name: Upload Release Asset
|
||||
id: upload-release-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||
asset_path: ${{ env.LINUX_APP_RELEASE_PATH }}/${{ env.LINUX_ZIP_NAME }}
|
||||
asset_name: ${{ env.LINUX_ZIP_NAME }}
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
- name: Upload Release Asset Install Package
|
||||
id: upload-release-asset-install-package
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||
asset_path: ${{ env.LINUX_APP_RELEASE_PATH }}/${{ env.LINUX_PACKAGE_NAME }}
|
||||
asset_name: ${{ env.LINUX_PACKAGE_NAME }}
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
build-macos-x86_64:
|
||||
runs-on: macos-latest
|
||||
needs: create-release
|
||||
env:
|
||||
MACOS_APP_RELEASE_PATH: frontend/app_flowy/product/${{ github.ref_name }}/macos/Release
|
||||
MACOS_X86_ZIP_NAME: Appflowy-macos-x86_64.zip
|
||||
MACOS_DMG_NAME: Appflowy-macos-x86_64-installer
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup environment - Rust and Cargo
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 'stable-2022-04-07'
|
||||
|
||||
- name: Setup environment - Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: 'stable'
|
||||
flutter-version: '3.0.5'
|
||||
|
||||
- name: Pre build
|
||||
working-directory: frontend
|
||||
run: |
|
||||
source $HOME/.cargo/env
|
||||
cargo install --force cargo-make
|
||||
cargo install --force duckscript_cli
|
||||
cargo make appflowy-deps-tools
|
||||
|
||||
- name: Build macOS app for x86_64
|
||||
working-directory: frontend
|
||||
run: |
|
||||
flutter config --enable-macos-desktop
|
||||
cargo make --env APP_VERSION=${{ github.ref_name }} --profile production-mac-x86_64 appflowy
|
||||
|
||||
- name: Create MacOS dmg
|
||||
working-directory: frontend
|
||||
run: |
|
||||
brew install create-dmg
|
||||
create-dmg \
|
||||
--volname ${{ env.MACOS_DMG_NAME }} \
|
||||
--hide-extension "AppFlowy.app" \
|
||||
--background scripts/dmg_assets/AppFlowyInstallerBackground.jpg \
|
||||
--window-size 600 450 \
|
||||
--icon-size 94 \
|
||||
--icon "AppFlowy.app" 141 249 \
|
||||
--app-drop-link 458 249 \
|
||||
"${{ env.MACOS_APP_RELEASE_PATH }}/${{ env.MACOS_DMG_NAME }}.dmg" \
|
||||
"${{ env.MACOS_APP_RELEASE_PATH }}/AppFlowy.app"
|
||||
|
||||
- name: Archive macOS app
|
||||
working-directory: ${{ env.MACOS_APP_RELEASE_PATH }}
|
||||
run: zip --symlinks -qr ${{ env.MACOS_X86_ZIP_NAME }} AppFlowy.app
|
||||
|
||||
- name: Upload Release Asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||
asset_path: ${{ env.MACOS_APP_RELEASE_PATH }}/${{ env.MACOS_X86_ZIP_NAME }}
|
||||
asset_name: ${{ env.MACOS_X86_ZIP_NAME }}
|
||||
asset_content_type: application/octet-stream
|
||||
- name: Upload DMG Release Asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||
asset_path: ${{ env.MACOS_APP_RELEASE_PATH }}/${{ env.MACOS_DMG_NAME }}.dmg
|
||||
asset_name: ${{ env.MACOS_DMG_NAME }}.dmg
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
build-windows-x86_64:
|
||||
runs-on: windows-latest
|
||||
build-for-windows:
|
||||
name: ${{ matrix.job.target }} (${{ matrix.job.os }})
|
||||
needs: create-release
|
||||
env:
|
||||
WINDOWS_APP_RELEASE_PATH: frontend\app_flowy\product\${{ github.ref_name }}\windows
|
||||
WINDOWS_ZIP_NAME: AppFlowy-windows-x86_64.zip
|
||||
WINDOWS_INSTALLER_NAME: AppFlowy-${{ github.ref_name }}-windows-x86_64
|
||||
WINDOWS_ZIP_NAME: AppFlowy_${{ github.ref_name }}_windows-x86_64.zip
|
||||
WINDOWS_INSTALLER_NAME: AppFlowy_${{ github.ref_name }}_windows-x86_64
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job:
|
||||
- { target: x86_64-pc-windows-msvc, os: windows-2019 }
|
||||
steps:
|
||||
- name: Checkout
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup environment - Rust and Cargo
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 'stable-2022-04-07'
|
||||
|
||||
- name: Setup environment - Flutter
|
||||
- name: Install flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: 'stable'
|
||||
flutter-version: '3.0.5'
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
- name: Pre build
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
target: ${{ matrix.job.target }}
|
||||
override: true
|
||||
components: rustfmt
|
||||
profile: minimal
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: appflowy-lib-cache
|
||||
key: ${{ matrix.job.os }}-${{ matrix.job.target }}
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
vcpkg integrate install
|
||||
cargo install --force cargo-make
|
||||
cargo install --force duckscript_cli
|
||||
cargo make appflowy-deps-tools
|
||||
|
||||
- name: Build Windows app
|
||||
working-directory: frontend
|
||||
@@ -243,11 +86,12 @@ jobs:
|
||||
flutter config --enable-windows-desktop
|
||||
cargo make --env APP_VERSION=${{ github.ref_name }} --profile production-windows-x86 appflowy
|
||||
|
||||
- uses: vimtor/action-zip@v1
|
||||
- name: Archive Asset
|
||||
uses: vimtor/action-zip@v1
|
||||
with:
|
||||
files: ${{ env.WINDOWS_APP_RELEASE_PATH }}\
|
||||
dest: ${{ env.WINDOWS_APP_RELEASE_PATH }}\${{ env.WINDOWS_ZIP_NAME }}
|
||||
|
||||
|
||||
- name: Copy installer config & icon file
|
||||
working-directory: frontend
|
||||
run: |
|
||||
@@ -258,7 +102,7 @@ jobs:
|
||||
run: |
|
||||
iscc /F${{ env.WINDOWS_INSTALLER_NAME }} inno_setup_config.iss /DAppVersion=${{ github.ref_name }}
|
||||
|
||||
- name: Upload Release Asset
|
||||
- name: Upload Asset
|
||||
id: upload-release-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
@@ -279,3 +123,232 @@ jobs:
|
||||
asset_path: ${{ env.WINDOWS_APP_RELEASE_PATH }}\Output\${{ env.WINDOWS_INSTALLER_NAME }}.exe
|
||||
asset_name: ${{ env.WINDOWS_INSTALLER_NAME }}.exe
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
build-for-macOS:
|
||||
name: ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-args }}]
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
needs: create-release
|
||||
env:
|
||||
MACOS_APP_RELEASE_PATH: frontend/app_flowy/product/${{ github.ref_name }}/macos/Release
|
||||
MACOS_X86_ZIP_NAME: AppFlowy_${{ github.ref_name }}_macos-x86_64.zip
|
||||
MACOS_DMG_NAME: AppFlowy_${{ github.ref_name }}_macos-x86_64
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job:
|
||||
- { target: x86_64-apple-darwin, os: macos-10.15, extra-build-args: "" }
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
target: ${{ matrix.job.target }}
|
||||
override: true
|
||||
components: rustfmt
|
||||
profile: minimal
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: appflowy-lib-cache
|
||||
key: ${{ matrix.job.os }}-${{ matrix.job.target }}
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo install --force cargo-make
|
||||
cargo install --force duckscript_cli
|
||||
|
||||
- name: Build AppFlowy
|
||||
working-directory: frontend
|
||||
run: |
|
||||
flutter config --enable-macos-desktop
|
||||
cargo make --env APP_VERSION=${{ github.ref_name }} --profile production-mac-x86_64 appflowy
|
||||
|
||||
- name: Create macOS dmg
|
||||
run: |
|
||||
brew install create-dmg
|
||||
create-dmg \
|
||||
--volname ${{ env.MACOS_DMG_NAME }} \
|
||||
--hide-extension "AppFlowy.app" \
|
||||
--background frontend/scripts/dmg_assets/AppFlowyInstallerBackground.jpg \
|
||||
--window-size 600 450 \
|
||||
--icon-size 94 \
|
||||
--icon "AppFlowy.app" 141 249 \
|
||||
--app-drop-link 458 249 \
|
||||
"${{ env.MACOS_APP_RELEASE_PATH }}/${{ env.MACOS_DMG_NAME }}.dmg" \
|
||||
"${{ env.MACOS_APP_RELEASE_PATH }}/AppFlowy.app"
|
||||
|
||||
- name: Archive Asset
|
||||
working-directory: ${{ env.MACOS_APP_RELEASE_PATH }}
|
||||
run: zip --symlinks -qr ${{ env.MACOS_X86_ZIP_NAME }} AppFlowy.app
|
||||
|
||||
- name: Upload Asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||
asset_path: ${{ env.MACOS_APP_RELEASE_PATH }}/${{ env.MACOS_X86_ZIP_NAME }}
|
||||
asset_name: ${{ env.MACOS_X86_ZIP_NAME }}
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
- name: Upload DMG Asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||
asset_path: ${{ env.MACOS_APP_RELEASE_PATH }}/${{ env.MACOS_DMG_NAME }}.dmg
|
||||
asset_name: ${{ env.MACOS_DMG_NAME }}.dmg
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
build-for-linux:
|
||||
name: ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-args }}]
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
needs: create-release
|
||||
env:
|
||||
LINUX_APP_RELEASE_PATH: frontend/app_flowy/product/${{ github.ref_name }}/linux/Release
|
||||
LINUX_ZIP_NAME: AppFlowy_${{ matrix.job.target }}_${{ matrix.job.os }}.tar.gz
|
||||
LINUX_PACKAGE_NAME: AppFlowy_${{ github.ref_name }}_${{ matrix.job.os }}.deb
|
||||
# PKG_CONFIG_SYSROOT_DIR: /
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job:
|
||||
- { arch: x86_64, target: x86_64-unknown-linux-gnu, os: ubuntu-20.04, extra-build-args: "", flutter_profile: production-linux-x86_64 }
|
||||
# - { arch: aarch64, target: aarch64-unknown-linux-gnu, os: ubuntu-20.04, extra-build-args: "", flutter_profile: production-linux-aarch64 }
|
||||
- { arch: x86_64, target: x86_64-unknown-linux-gnu, os: ubuntu-18.04, extra-build-args: "", flutter_profile: production-linux-x86_64}
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
target: ${{ matrix.job.target }}
|
||||
override: true
|
||||
components: rustfmt
|
||||
profile: minimal
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: appflowy-lib-cache
|
||||
key: ${{ matrix.job.os }}-${{ matrix.job.target }}
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential libsqlite3-dev libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
|
||||
sudo apt-get install keybinder-3.0
|
||||
source $HOME/.cargo/env
|
||||
cargo install --force cargo-make
|
||||
cargo install --force duckscript_cli
|
||||
rustup target add ${{ matrix.job.target }}
|
||||
|
||||
- name: Install gcc-aarch64-linux-gnu
|
||||
if: ${{ matrix.job.target == 'aarch64-unknown-linux-gnu' }}
|
||||
working-directory: frontend
|
||||
run: |
|
||||
sudo apt-get install -qy binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
|
||||
|
||||
- name: Build AppFlowy
|
||||
working-directory: frontend
|
||||
run: |
|
||||
flutter config --enable-linux-desktop
|
||||
cargo make --env APP_VERSION=${{ github.ref_name }} --profile ${{ matrix.job.flutter_profile}} appflowy
|
||||
|
||||
- name: Archive Assert
|
||||
working-directory: ${{ env.LINUX_APP_RELEASE_PATH }}
|
||||
run: tar -czf ${{ env.LINUX_ZIP_NAME }} *
|
||||
|
||||
- name: Configuring Linux Package
|
||||
working-directory: frontend
|
||||
run: |
|
||||
mkdir -p ../${{ env.LINUX_APP_RELEASE_PATH }}/package/opt
|
||||
mkdir -p ../${{ env.LINUX_APP_RELEASE_PATH }}/package/usr/share/applications
|
||||
cp -r ./scripts/linux_installer ../${{ env.LINUX_APP_RELEASE_PATH }}/package/DEBIAN
|
||||
cd ../${{ env.LINUX_APP_RELEASE_PATH }}/package/DEBIAN
|
||||
grep -rl "\[CHANGE_THIS\]" ./control | xargs sed -i "s/\[CHANGE_THIS\]/${{ github.ref_name }}/"
|
||||
chmod 0755 {postinst,postrm}
|
||||
|
||||
- name: Build Linux package
|
||||
working-directory: ${{ env.LINUX_APP_RELEASE_PATH }}
|
||||
run: |
|
||||
mv AppFlowy package/opt/
|
||||
cd package
|
||||
|
||||
# Update Exec & icon path in desktop entry
|
||||
grep -rl "\[CHANGE_THIS\]" ./opt/AppFlowy/appflowy.desktop.temp | xargs sed -i "s/\[CHANGE_THIS\]/\/opt/"
|
||||
# Add desktop entry in package
|
||||
mv ./opt/AppFlowy/appflowy.desktop.temp ./usr/share/applications/appflowy.desktop
|
||||
|
||||
# Build
|
||||
cd ../ && dpkg-deb --build --root-owner-group -Z xz package ${{ env.LINUX_PACKAGE_NAME }}
|
||||
|
||||
- name: Upload Asset
|
||||
id: upload-release-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||
asset_path: ${{ env.LINUX_APP_RELEASE_PATH }}/${{ env.LINUX_ZIP_NAME }}
|
||||
asset_name: ${{ env.LINUX_ZIP_NAME }}
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
- name: Upload Asset Install Package
|
||||
id: upload-release-asset-install-package
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||
asset_path: ${{ env.LINUX_APP_RELEASE_PATH }}/${{ env.LINUX_PACKAGE_NAME }}
|
||||
asset_name: ${{ env.LINUX_PACKAGE_NAME }}
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
build-for-docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ./frontend/scripts/docker-buildfiles/Dockerfile
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
push: true
|
||||
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/appflowy_client:${{ github.ref_name }}
|
||||
cache-from: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/af_build_cache:buildcache
|
||||
cache-to: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/af_build_cache:buildcache,mode=max
|
||||
@@ -1,35 +0,0 @@
|
||||
name: release_docker
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build-docker-image:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ./frontend/scripts/docker-buildfiles/Dockerfile
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
push: true
|
||||
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/appflowy_client:${{ github.ref_name }}
|
||||
cache-from: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/af_build_cache:buildcache
|
||||
cache-to: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/af_build_cache:buildcache,mode=max
|
||||
@@ -0,0 +1,86 @@
|
||||
name: Rust-CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/rust-lib/**"
|
||||
- "shared-lib/**"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/rust-lib/**"
|
||||
- "shared-lib/**"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUST_TOOLCHAIN: "1.65"
|
||||
FLUTTER_VERSION: "3.3.9"
|
||||
|
||||
jobs:
|
||||
test-on-ubuntu:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
profile: minimal
|
||||
|
||||
- name: Install flutter
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo install --force cargo-make
|
||||
cargo install --force duckscript_cli
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: ${{ matrix.job.os }}
|
||||
|
||||
- name: Build FlowySDK
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make --profile development-linux-x86_64 appflowy-core-dev
|
||||
|
||||
- name: rustfmt rust-lib
|
||||
run: cargo fmt --all -- --check
|
||||
working-directory: frontend/rust-lib/
|
||||
|
||||
- name: clippy rust-lib
|
||||
run: cargo clippy --features="rev-sqlite"
|
||||
working-directory: frontend/rust-lib
|
||||
|
||||
- name: Run rust-lib tests
|
||||
working-directory: frontend/rust-lib
|
||||
run: RUST_LOG=info cargo test --no-default-features --features="sync,rev-sqlite"
|
||||
|
||||
- name: rustfmt shared-lib
|
||||
run: cargo fmt --all -- --check
|
||||
working-directory: shared-lib
|
||||
|
||||
- name: clippy shared-lib
|
||||
run: cargo clippy -- -D warnings
|
||||
working-directory: shared-lib
|
||||
|
||||
- name: Run shared-lib tests
|
||||
working-directory: shared-lib
|
||||
run: RUST_LOG=info cargo test --no-default-features
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Rust coverage tests
|
||||
name: Rust code coverage
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -9,65 +9,54 @@ on:
|
||||
- "frontend/rust-lib/**"
|
||||
- "shared-lib/**"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/rust-lib/**"
|
||||
- "shared-lib/**"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
FLUTTER_VERSION: "3.3.9"
|
||||
RUST_TOOLCHAIN: "1.65"
|
||||
|
||||
jobs:
|
||||
test-coverage:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- id: rust_toolchain
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 'stable-2022-04-07'
|
||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
target: ${{ matrix.job.target }}
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- name: Cache Cargo
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cargo
|
||||
key: ${{ runner.os }}-cargo-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
|
||||
- name: Install flutter
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
- name: Cache Rust
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
frontend/rust-lib/target
|
||||
shared-lib/target
|
||||
key: ${{ runner.os }}-rust-rust-lib-share-lib-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
|
||||
|
||||
- name: Setup Environment
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dart curl build-essential libsqlite3-dev libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
|
||||
sudo apt-get install keybinder-3.0
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
echo 'do nothing'
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Install cargo-make, grcov and llvm-tools-preview
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo install cargo-make
|
||||
cargo install --force cargo-make
|
||||
cargo install --force duckscript_cli
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: ${{ matrix.job.os }}
|
||||
|
||||
- name: Install code-coverage tools
|
||||
working-directory: frontend
|
||||
run: |
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo apt-get update
|
||||
sudo apt-get install keybinder-3.0
|
||||
cargo install grcov
|
||||
rustup component add llvm-tools-preview
|
||||
|
||||
- name: Run Coverage tests and generate LCOV report
|
||||
- name: Run tests
|
||||
working-directory: frontend
|
||||
run: cargo make get_ci_test_coverage
|
||||
run: cargo make rust_unit_test_with_coverage
|
||||
@@ -1,61 +0,0 @@
|
||||
name: Rust lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/rust-lib/**"
|
||||
- "shared-lib/**"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/rust-lib/**"
|
||||
- "shared-lib/**"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
rust-fmt:
|
||||
name: Rustfmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 'stable-2022-04-07'
|
||||
override: true
|
||||
- uses: subosito/flutter-action@v1
|
||||
with:
|
||||
flutter-version: '3.0.5'
|
||||
channel: "stable"
|
||||
|
||||
- name: Rust Deps
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo install cargo-make
|
||||
cargo make appflowy-deps-tools
|
||||
|
||||
- name: Build FlowySDK
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make --profile development-linux-x86_64 appflowy-sdk-dev
|
||||
|
||||
- run: rustup component add rustfmt
|
||||
working-directory: frontend/rust-lib
|
||||
- name: rustfmt
|
||||
run: cargo fmt --all -- --check
|
||||
working-directory: frontend/rust-lib/
|
||||
|
||||
|
||||
- run: rustup component add clippy
|
||||
working-directory: frontend/rust-lib
|
||||
- name: clippy
|
||||
run: cargo clippy --no-default-features
|
||||
working-directory: frontend/rust-lib
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
name: Backend test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/rust-lib/**"
|
||||
- "shared-lib/**"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/rust-lib/**"
|
||||
- "shared-lib/**"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- id: rust_toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 'stable-2022-04-07'
|
||||
|
||||
- name: Cache Cargo
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cargo
|
||||
key: ${{ runner.os }}-cargo-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
|
||||
|
||||
- name: Cache Rust
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
frontend/rust-lib/target
|
||||
shared-lib/target
|
||||
key: ${{ runner.os }}-rust-rust-lib-share-lib-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
|
||||
|
||||
- name: Install cargo-make
|
||||
working-directory: frontend
|
||||
run: cargo install cargo-make
|
||||
|
||||
- name: Run rust-lib tests
|
||||
working-directory: frontend/rust-lib
|
||||
run: RUST_LOG=info cargo test --no-default-features --features="sync"
|
||||
|
||||
- name: Run shared-lib tests
|
||||
working-directory: shared-lib
|
||||
run: RUST_LOG=info cargo test --no-default-features
|
||||
+1
-2
@@ -19,10 +19,8 @@ node_modules
|
||||
**/.cache
|
||||
**/.DS_Store
|
||||
|
||||
**/src/protobuf
|
||||
**/resources/proto
|
||||
|
||||
frontend/.vscode/*
|
||||
!frontend/.vscode/settings.json
|
||||
!frontend/.vscode/tasks.json
|
||||
!frontend/.vscode/launch.json
|
||||
@@ -36,3 +34,4 @@ pubspec.lock
|
||||
# ignore tool used for commit linting
|
||||
.githooks/gitlint
|
||||
.githooks/gitlint.exe
|
||||
.fvm/
|
||||
+105
-24
@@ -1,58 +1,131 @@
|
||||
# Release Notes
|
||||
## Version 0.0.7 - 11/27/2022
|
||||
|
||||
## Version 0.1.0 - 02/09/2023
|
||||
|
||||
### New features
|
||||
|
||||
- Support linking a Board or Grid into the Document page
|
||||
- Integrate a callout plugin implemented by community members
|
||||
- Optimize user interface
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix some bugs
|
||||
|
||||
## Version 0.0.9.1 - 01/03/2023
|
||||
|
||||
### New features
|
||||
|
||||
- New theme
|
||||
- Support changing text color of editor
|
||||
- Optimize user interface
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix some grid bugs
|
||||
|
||||
## Version 0.0.9 - 12/21/2022
|
||||
|
||||
### New features
|
||||
|
||||
- Enable the user to define where to store their data
|
||||
- Support inserting Emojis through the slash command
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix some bugs
|
||||
|
||||
## Version 0.0.8.1 - 12/09/2022
|
||||
|
||||
### New features
|
||||
|
||||
- Support following your default system theme
|
||||
- Improve the filter in Grid
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Copy/Paste
|
||||
|
||||
## Version 0.0.8 - 11/30/2022
|
||||
|
||||
### New features
|
||||
|
||||
- Table-view database
|
||||
- support column type: Checklist
|
||||
- Board-view database
|
||||
- support column type: Checklist
|
||||
- Customize font size: small, medium, large
|
||||
|
||||
## Version 0.0.7.1 - 11/30/2022
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix some bugs
|
||||
|
||||
## Version 0.0.7 - 11/27/2022
|
||||
|
||||
### New features
|
||||
|
||||
- Support adding filters by the text/checkbox/single-select property in Grid
|
||||
|
||||
## Version 0.0.6.2 - 10/30/2022
|
||||
|
||||
- Fix some bugs
|
||||
|
||||
## Version 0.0.6.1 - 10/26/2022
|
||||
|
||||
### New features
|
||||
|
||||
- Optimize appflowy_editor dark mode style
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Unable to copy the text with checkbox or link style
|
||||
|
||||
## Version 0.0.6 - 10/23/2022
|
||||
|
||||
### New features
|
||||
- Integrate **appflowy_editor**
|
||||
|
||||
- Integrate **appflowy_editor**
|
||||
|
||||
## Version 0.0.5.3 - 09/26/2022
|
||||
|
||||
### New features
|
||||
|
||||
- Open the next page automatically after deleting the current page
|
||||
- Refresh the Kanban board after altering a property type
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix switch board bug
|
||||
- Fix delete the Kanban board's row error
|
||||
- Remove duplicate time format
|
||||
- Fix can't delete field in property edit panel
|
||||
- Adjust some display UI issues
|
||||
|
||||
|
||||
## Version 0.0.5.2 - 09/16/2022
|
||||
|
||||
### New features
|
||||
|
||||
- Enable adding a new card to the "No Status" group
|
||||
- Fix some bugs
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix cannot open AppFlowy error
|
||||
- Fix delete the Kanban board's row error
|
||||
|
||||
|
||||
## Version 0.0.5.1 - 09/14/2022
|
||||
|
||||
### New features
|
||||
- Enable deleting a field in board
|
||||
|
||||
- Enable deleting a field in board
|
||||
- Fix some bugs
|
||||
|
||||
|
||||
## Version 0.0.5 - 09/08/2022
|
||||
|
||||
### New features - Kanban Board like Notion and Trello beta
|
||||
|
||||
Boards are the best way to manage projects & tasks. Use them to group your databases by select, multiselect, and checkbox.
|
||||
|
||||
<p align="left"><img src="https://user-images.githubusercontent.com/12026239/190055984-6efa2d7a-ee38-4551-859e-ee56388e1859.gif" width="1000px" /></p>
|
||||
@@ -62,6 +135,7 @@ Boards are the best way to manage projects & tasks. Use them to group your datab
|
||||
- Update database properties in the Board view by clicking on a property and making edits on the card
|
||||
|
||||
### Other Features & Improvements
|
||||
|
||||
- Settings allow users to change avatars
|
||||
- Click and drag the right edge to resize your sidebar
|
||||
- And many user interface improvements (link)
|
||||
@@ -69,27 +143,30 @@ Boards are the best way to manage projects & tasks. Use them to group your datab
|
||||
## Version 0.0.5 - beta.2 - beta.1 - 09/01/2022
|
||||
|
||||
### New features
|
||||
|
||||
- Board-view database
|
||||
- Support start editing after creating a new card
|
||||
- Support editing the card directly by clicking the edit button
|
||||
- Add the `No Status` column to display the cards while their status is empty
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Optimize insert card animation
|
||||
- Fix some UI bugs
|
||||
|
||||
## Version 0.0.5 - beta.1 - 08/25/2022
|
||||
|
||||
### New features
|
||||
- Board-view database
|
||||
|
||||
- Board-view database
|
||||
- Group by single select
|
||||
- drag and drop cards
|
||||
- insert / delete cards
|
||||
|
||||

|
||||
|
||||
|
||||
## Version 0.0.4 - 06/06/2022
|
||||
|
||||
- Drag to adjust the width of a column
|
||||
- Upgrade to Flutter 3.0
|
||||
- Native support for M1 chip
|
||||
@@ -98,10 +175,11 @@ Boards are the best way to manage projects & tasks. Use them to group your datab
|
||||
- Keyboard shortcuts support for Grid: press Enter to leave the edit mode; control c/v to copy-paste cell values
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed some bugs
|
||||
|
||||
|
||||
## Version 0.0.4 - beta.3 - 05/02/2022
|
||||
|
||||
- Drag to reorder app/ view/ field
|
||||
- Row record opens as a page
|
||||
- Auto resize the height of the row in the grid
|
||||
@@ -110,42 +188,45 @@ Boards are the best way to manage projects & tasks. Use them to group your datab
|
||||
|
||||

|
||||
|
||||
|
||||
### Bug Fixes & Improvements
|
||||
|
||||
- Improved row/cell data cache
|
||||
- Fixed some bugs
|
||||
|
||||
|
||||
## Version 0.0.4 - beta.2 - 04/11/2022
|
||||
|
||||
- Support properties: Text, Number, Date, Checkbox, Select, Multi-select
|
||||
- Insert / delete rows
|
||||
- Add / delete / hide columns
|
||||
- Edit property
|
||||

|
||||
- Support properties: Text, Number, Date, Checkbox, Select, Multi-select
|
||||
- Insert / delete rows
|
||||
- Add / delete / hide columns
|
||||
- Edit property
|
||||

|
||||
|
||||
## Version 0.0.4 - beta.1 - 04/08/2022
|
||||
|
||||
v0.0.4 - beta.1 is pre-release
|
||||
|
||||
### New features
|
||||
|
||||
- Table-view database
|
||||
- supported column types: Text, Checkbox, Single-select, Multi-select, Numbers
|
||||
- hide / delete columns
|
||||
- insert rows
|
||||
- support column types: Text, Checkbox, Single-select, Multi-select, Numbers
|
||||
- hide / delete columns
|
||||
- insert rows
|
||||
|
||||
## Version 0.0.3 - 02/23/2022
|
||||
|
||||
v0.0.3 is production ready, available on Linux, macOS, and Windows
|
||||
|
||||
### New features
|
||||
- Dark Mode
|
||||
- Support new languages: French, Italian, Russian, Simplified Chinese, Spanish
|
||||
|
||||
- Dark Mode
|
||||
- Support new languages: French, Italian, Russian, Simplified Chinese, Spanish
|
||||
- Add Settings: Toggle on Dark Mode; Select a language
|
||||
- Show device info
|
||||
- Show device info
|
||||
- Add tooltip on the toolbar icons
|
||||
|
||||
Bug fixes and improvements
|
||||
|
||||
- Increased height of action
|
||||
- CPU performance issue
|
||||
- CPU performance issue
|
||||
- Fix potential data parser error
|
||||
- More foundation work for online collaboration
|
||||
|
||||
Vendored
+54
-50
@@ -6,37 +6,20 @@
|
||||
"configurations": [
|
||||
{
|
||||
// This task builds the Rust and Dart code of AppFlowy.
|
||||
"name": "AF: Build All",
|
||||
"name": "AF-desktop: Build All",
|
||||
"request": "launch",
|
||||
"program": "./lib/main.dart",
|
||||
"type": "dart",
|
||||
"preLaunchTask": "AF: build_flowy_sdk",
|
||||
"preLaunchTask": "AF: Build Appflowy Core",
|
||||
"env": {
|
||||
"RUST_LOG": "info"
|
||||
"RUST_LOG": "trace",
|
||||
// "RUST_LOG": "debug"
|
||||
},
|
||||
"cwd": "${workspaceRoot}/app_flowy"
|
||||
},
|
||||
{
|
||||
// This task builds the Rust and Dart code of AppFlowy for android.
|
||||
"name": "AF: Run Android",
|
||||
"request": "launch",
|
||||
"program": "./lib/main.dart",
|
||||
"type": "dart",
|
||||
"preLaunchTask": "AF: build_mobile_sdk",
|
||||
"env": {
|
||||
"RUST_LOG": "info"
|
||||
},
|
||||
"cwd": "${workspaceRoot}/app_flowy"
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"name": "AF-desktop: Build Dart Only",
|
||||
"request": "launch",
|
||||
"program": "./lib/main.dart",
|
||||
"type": "dart",
|
||||
@@ -50,7 +33,7 @@
|
||||
// - call the clean task,
|
||||
// - rebuild all the generated Files (including freeze and language files)
|
||||
// - rebuild the the Rust and Dart code of AppFlowy.
|
||||
"name": "AF: Clean + Rebuild All",
|
||||
"name": "AF-desktop: Clean + Rebuild All",
|
||||
"request": "launch",
|
||||
"program": "./lib/main.dart",
|
||||
"type": "dart",
|
||||
@@ -60,12 +43,38 @@
|
||||
},
|
||||
"cwd": "${workspaceRoot}/app_flowy"
|
||||
},
|
||||
{
|
||||
"name": "AF-desktop: Debug Rust",
|
||||
"request": "attach",
|
||||
"type": "lldb",
|
||||
"pid": "${command:pickMyProcess}"
|
||||
},
|
||||
// {
|
||||
// "name": "AF-desktop: profile mode",
|
||||
// "request": "launch",
|
||||
// "program": "./lib/main.dart",
|
||||
// "type": "dart",
|
||||
// "flutterMode": "profile",
|
||||
// "cwd": "${workspaceRoot}/app_flowy"
|
||||
// },
|
||||
{
|
||||
// This task builds the Rust and Dart code of AppFlowy for android.
|
||||
"name": "AF-android: Build All",
|
||||
"request": "launch",
|
||||
"program": "./lib/main.dart",
|
||||
"type": "dart",
|
||||
"preLaunchTask": "AF: build_mobile_sdk",
|
||||
"env": {
|
||||
"RUST_LOG": "info"
|
||||
},
|
||||
"cwd": "${workspaceRoot}/app_flowy"
|
||||
},
|
||||
{
|
||||
// This task builds will:
|
||||
// - call the clean task,
|
||||
// - rebuild all the generated Files (including freeze and language files)
|
||||
// - rebuild the the Rust and Dart code of AppFlowy.
|
||||
"name": "AF: Clean + Rebuild All (Android)",
|
||||
"name": "AF-android: Clean + Rebuild All",
|
||||
"request": "launch",
|
||||
"program": "./lib/main.dart",
|
||||
"type": "dart",
|
||||
@@ -76,34 +85,29 @@
|
||||
"cwd": "${workspaceRoot}/app_flowy"
|
||||
},
|
||||
{
|
||||
"name": "AF: Build All (rustlog: trace)",
|
||||
// https://tauri.app/v1/guides/debugging/vs-code
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"program": "./lib/main.dart",
|
||||
"type": "dart",
|
||||
"preLaunchTask": "AF: build_flowy_sdk",
|
||||
"env": {
|
||||
"RUST_LOG": "trace"
|
||||
"name": "AF-tauri: Debug backend",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"build",
|
||||
"--manifest-path=./appflowy_tauri/src-tauri/Cargo.toml",
|
||||
"--no-default-features"
|
||||
]
|
||||
},
|
||||
"cwd": "${workspaceRoot}/app_flowy"
|
||||
},
|
||||
{
|
||||
"name": "AF: Build All Android (rustlog: trace)",
|
||||
"request": "launch",
|
||||
"program": "./lib/main.dart",
|
||||
"type": "dart",
|
||||
"preLaunchTask": "AF: build_mobile_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"
|
||||
"preLaunchTask": "AF: Tauri UI Dev",
|
||||
"cwd": "${workspaceRoot}/appflowy_tauri/"
|
||||
},
|
||||
// {
|
||||
// "type": "lldb",
|
||||
// "request": "launch",
|
||||
// "name": "AF-tauri: Production Debug",
|
||||
// "cargo": {
|
||||
// "args": ["build", "--release", "--manifest-path=./appflowy_tauri/src-tauri/Cargo.toml"]
|
||||
// },
|
||||
// "preLaunchTask": "AF: Tauri UI Build",
|
||||
// "cwd": "${workspaceRoot}/appflowy_tauri/"
|
||||
// },
|
||||
]
|
||||
}
|
||||
Vendored
+10
@@ -9,6 +9,16 @@
|
||||
"editor.tabCompletion": "onlySnippets",
|
||||
"editor.wordBasedSuggestions": false,
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.rulers": [80],
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.rulers": [80],
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"svgviewer.enableautopreview": true,
|
||||
"svgviewer.previewcolumn": "Active",
|
||||
"svgviewer.showzoominout": true,
|
||||
|
||||
Vendored
+65
-37
@@ -14,13 +14,13 @@
|
||||
"type": "shell",
|
||||
"dependsOrder": "sequence",
|
||||
"dependsOn": [
|
||||
"AF: Rust Clean",
|
||||
"AF: Dart Clean",
|
||||
"AF: Flutter Clean",
|
||||
"AF: build_flowy_sdk",
|
||||
"AF: Build Appflowy Core",
|
||||
"AF: Flutter Pub Get",
|
||||
"AF: Flutter Package Get",
|
||||
"AF: Generate Language Files",
|
||||
"AF: Generate Freezed Files",
|
||||
"AF: Generate Freezed Files"
|
||||
],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
@@ -32,60 +32,39 @@
|
||||
"type": "shell",
|
||||
"dependsOrder": "sequence",
|
||||
"dependsOn": [
|
||||
"AF: Rust Clean",
|
||||
"AF: Dart Clean",
|
||||
"AF: Flutter Clean",
|
||||
"AF: build_flowy_sdk_for_android",
|
||||
"AF: Build Appflowy Core_for_android",
|
||||
"AF: Flutter Pub Get",
|
||||
"AF: Flutter Package Get",
|
||||
"AF: Generate Language Files",
|
||||
"AF: Generate Freezed Files",
|
||||
"AF: Generate Freezed Files"
|
||||
],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new",
|
||||
},
|
||||
"panel": "new"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: build_flowy_sdk_for_android",
|
||||
"label": "AF: Build Appflowy Core_for_android",
|
||||
"type": "shell",
|
||||
"command": "cargo make --profile development-android appflowy-sdk-dev-android",
|
||||
"command": "cargo make --profile development-android appflowy-core-dev-android",
|
||||
"group": "build",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: build_flowy_sdk",
|
||||
"label": "AF: Build Appflowy Core",
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
"command": "cargo make --profile development-windows-x86 appflowy-core-dev"
|
||||
},
|
||||
"linux": {
|
||||
"options": {
|
||||
"env": {
|
||||
"FLOWY_DEV_ENV": "Linux"
|
||||
}
|
||||
}
|
||||
"command": "cargo make --profile \"development-linux-$(uname -m)\" appflowy-core-dev"
|
||||
},
|
||||
"osx": {
|
||||
"options": {
|
||||
"env": {
|
||||
"FLOWY_DEV_ENV": "macOS"
|
||||
}
|
||||
}
|
||||
"command": "cargo make --profile \"development-mac-$(uname -m)\" appflowy-core-dev"
|
||||
},
|
||||
"group": "build",
|
||||
"options": {
|
||||
@@ -166,9 +145,9 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Rust Clean",
|
||||
"label": "AF: Flutter Clean",
|
||||
"type": "shell",
|
||||
"command": "cargo make flowy_clean",
|
||||
"command": "cargo make flutter_clean",
|
||||
"group": "build",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}"
|
||||
@@ -185,6 +164,55 @@
|
||||
"group": "build",
|
||||
"problemMatcher": [],
|
||||
"detail": "app_flowy"
|
||||
},
|
||||
{
|
||||
"label": "AF: Tauri UI Dev",
|
||||
"type": "shell",
|
||||
"isBackground": true,
|
||||
"command": "npm run dev",
|
||||
"problemMatcher": ["$tsc"],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/appflowy_tauri"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Tauri UI Build",
|
||||
"type": "shell",
|
||||
"command": "npm run build",
|
||||
"problemMatcher": ["$tsc"],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/appflowy_tauri"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Tauri Dev",
|
||||
"type": "shell",
|
||||
"command": "npm run tauri dev",
|
||||
"problemMatcher": ["$tsc"],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/appflowy_tauri"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Tauri Clean",
|
||||
"type": "shell",
|
||||
"command": "cargo make tauri_clean",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
"problemMatcher": ["$tsc"]
|
||||
},
|
||||
{
|
||||
"label": "AF: Tauri Clean + Dev",
|
||||
"type": "shell",
|
||||
"dependsOrder": "sequence",
|
||||
"dependsOn": [
|
||||
"AF: Tauri Clean",
|
||||
"AF: Tauri UI Dev"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+23
-22
@@ -1,13 +1,14 @@
|
||||
#https://github.com/sagiegurari/cargo-make
|
||||
|
||||
extend = [
|
||||
{ path = "scripts/makefile/desktop.toml" },
|
||||
{ path = "scripts/makefile/protobuf.toml" },
|
||||
{ path = "scripts/makefile/tests.toml" },
|
||||
{ path = "scripts/makefile/docker.toml" },
|
||||
{ path = "scripts/makefile/env.toml" },
|
||||
{ path = "scripts/makefile/flutter.toml" },
|
||||
{ path = "scripts/makefile/tool.toml" },
|
||||
{ path = "scripts/makefile/desktop.toml" },
|
||||
{ path = "scripts/makefile/protobuf.toml" },
|
||||
{ path = "scripts/makefile/tests.toml" },
|
||||
{ path = "scripts/makefile/docker.toml" },
|
||||
{ path = "scripts/makefile/env.toml" },
|
||||
{ path = "scripts/makefile/flutter.toml" },
|
||||
{ path = "scripts/makefile/tool.toml" },
|
||||
{ path = "scripts/makefile/tauri.toml" },
|
||||
]
|
||||
|
||||
[config]
|
||||
@@ -17,17 +18,17 @@ on_error_task = "catch"
|
||||
run_task = { name = ["restore-crate-type"] }
|
||||
|
||||
[env]
|
||||
RUST_LOG = "info"
|
||||
RUST_LOG = "debug"
|
||||
CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
|
||||
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
|
||||
CARGO_MAKE_CRATE_NAME = "dart-ffi"
|
||||
LIB_NAME = "dart_ffi"
|
||||
CURRENT_APP_VERSION = "0.0.7"
|
||||
FEATURES = "flutter"
|
||||
CURRENT_APP_VERSION = "0.1.0"
|
||||
FLUTTER_DESKTOP_FEATURES = "flutter,rev-sqlite"
|
||||
PRODUCT_NAME = "AppFlowy"
|
||||
# CRATE_TYPE: https://doc.rust-lang.org/reference/linkage.html
|
||||
# If you update the macOS's CRATE_TYPE, don't forget to update the
|
||||
# flowy_sdk.podspec
|
||||
# appflowy_backend.podspec
|
||||
# for staticlib:
|
||||
# s.static_framework = true
|
||||
# s.vendored_libraries = "libdart_ffi.a"
|
||||
@@ -42,8 +43,8 @@ PRODUCT_NAME = "AppFlowy"
|
||||
CRATE_TYPE = "staticlib"
|
||||
LIB_EXT = "a"
|
||||
APP_ENVIRONMENT = "local"
|
||||
FLUTTER_FLOWY_SDK_PATH = "app_flowy/packages/flowy_sdk"
|
||||
PROTOBUF_DERIVE_CACHE = "../shared-lib/flowy-derive/src/derive_cache/derive_cache.rs"
|
||||
FLUTTER_FLOWY_SDK_PATH = "app_flowy/packages/appflowy_backend"
|
||||
TAURI_BACKEND_SERVICE_PATH = "appflowy_tauri/src/services/backend"
|
||||
# Test default config
|
||||
TEST_CRATE_TYPE = "cdylib"
|
||||
TEST_LIB_EXT = "dylib"
|
||||
@@ -133,6 +134,7 @@ CRATE_TYPE = "cdylib"
|
||||
FLUTTER_OUTPUT_DIR = "Debug"
|
||||
LIB_EXT = "so"
|
||||
LINUX_ARCH = "arm64"
|
||||
FLUTTER_DESKTOP_FEATURES = "flutter,rev-sqlite,openssl_vendored"
|
||||
|
||||
[env.production-linux-aarch64]
|
||||
BUILD_FLAG = "release"
|
||||
@@ -143,6 +145,7 @@ FLUTTER_OUTPUT_DIR = "Release"
|
||||
LIB_EXT = "so"
|
||||
LINUX_ARCH = "arm64"
|
||||
APP_ENVIRONMENT = "production"
|
||||
FLUTTER_DESKTOP_FEATURES = "flutter,rev-sqlite,openssl_vendored"
|
||||
|
||||
[tasks.echo_env]
|
||||
script = ['''
|
||||
@@ -151,7 +154,7 @@ script = ['''
|
||||
echo BUILD_FLAG: ${BUILD_FLAG}
|
||||
echo TARGET_OS: ${TARGET_OS}
|
||||
echo RUST_COMPILE_TARGET: ${RUST_COMPILE_TARGET}
|
||||
echo FEATURES: ${FEATURES}
|
||||
echo FEATURES: ${FLUTTER_DESKTOP_FEATURES}
|
||||
echo PRODUCT_EXT: ${PRODUCT_EXT}
|
||||
echo APP_ENVIRONMENT: ${APP_ENVIRONMENT}
|
||||
echo ${platforms}
|
||||
@@ -170,19 +173,19 @@ BUILD_FLAG = "debug"
|
||||
TARGET_OS = "android"
|
||||
CRATE_TYPE = "cdylib"
|
||||
FLUTTER_OUTPUT_DIR = "Debug"
|
||||
FEATURES = "flutter,openssl_vendored"
|
||||
FLUTTER_DESKTOP_FEATURES = "flutter,rev-sqlite,openssl_vendored"
|
||||
|
||||
[env.production-android]
|
||||
BUILD_FLAG = "release"
|
||||
TARGET_OS = "android"
|
||||
CRATE_TYPE = "cdylib"
|
||||
FLUTTER_OUTPUT_DIR = "Release"
|
||||
FEATURES = "flutter,openssl_vendored"
|
||||
FLUTTER_DESKTOP_FEATURES = "flutter,rev-sqlite,openssl_vendored"
|
||||
|
||||
[tasks.setup-crate-type]
|
||||
private = true
|
||||
script = [
|
||||
"""
|
||||
"""
|
||||
toml = readfile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml
|
||||
val = replace ${toml} "staticlib" ${CRATE_TYPE}
|
||||
result = writefile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml ${val}
|
||||
@@ -194,7 +197,7 @@ script_runner = "@duckscript"
|
||||
[tasks.restore-crate-type]
|
||||
private = true
|
||||
script = [
|
||||
"""
|
||||
"""
|
||||
toml = readfile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml
|
||||
val = replace ${toml} ${CRATE_TYPE} "staticlib"
|
||||
result = writefile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml ${val}
|
||||
@@ -222,7 +225,7 @@ TEST_COMPILE_TARGET = "x86_64-pc-windows-msvc"
|
||||
[tasks.setup-test-crate-type]
|
||||
private = true
|
||||
script = [
|
||||
"""
|
||||
"""
|
||||
toml = readfile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml
|
||||
val = replace ${toml} "staticlib" ${TEST_CRATE_TYPE}
|
||||
result = writefile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml ${val}
|
||||
@@ -234,7 +237,7 @@ script_runner = "@duckscript"
|
||||
[tasks.restore-test-crate-type]
|
||||
private = true
|
||||
script = [
|
||||
"""
|
||||
"""
|
||||
toml = readfile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml
|
||||
val = replace ${toml} ${TEST_CRATE_TYPE} "staticlib"
|
||||
result = writefile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml ${val}
|
||||
@@ -250,5 +253,3 @@ script = ["""
|
||||
cargo build -vv --features=dart
|
||||
"""]
|
||||
script_runner = "@shell"
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 14.0039C11.3137 14.0039 14 11.3176 14 8.00391C14 4.6902 11.3137 2.00391 8 2.00391C4.68629 2.00391 2 4.6902 2 8.00391C2 11.3176 4.68629 14.0039 8 14.0039Z" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<rect x="7.5" y="7.00391" width="1" height="4" rx="0.5" fill="#333333"/>
|
||||
<rect x="7.5" y="5.00391" width="1" height="1" rx="0.5" fill="#333333"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 482 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.01471 2.15012C7.62441 1.7981 8.37559 1.7981 8.98529 2.15012L12.5769 4.22372C13.1866 4.57573 13.5622 5.22628 13.5622 5.9303V10.0775C13.5622 10.7815 13.1866 11.4321 12.5769 11.7841L8.98529 13.8577C8.37559 14.2097 7.62441 14.2097 7.01471 13.8577L3.42312 11.7841C2.81341 11.4321 2.43782 10.7815 2.43782 10.0775V5.9303C2.43782 5.22628 2.81341 4.57573 3.42312 4.22372L7.01471 2.15012Z" stroke="#333333"/>
|
||||
<circle cx="8" cy="8.00391" r="2.5" stroke="#333333"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 569 B |
@@ -138,12 +138,16 @@
|
||||
"open": "Obrir la configuració"
|
||||
},
|
||||
"appearance": {
|
||||
"lightLabel": "Mode Clar",
|
||||
"darkLabel": "Mode Fosc"
|
||||
"themeMode": {
|
||||
"label": "Theme Mode",
|
||||
"light": "Mode Clar",
|
||||
"dark": "Mode Fosc",
|
||||
"system": "Adapt to System"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sideBar": {
|
||||
"openSidebar": "Open sidebar",
|
||||
"closeSidebar": "Close sidebar"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
"welcomeText": "Welcome to @:appName",
|
||||
"githubStarText": "Star on GitHub",
|
||||
"subscribeNewsletterText": "Subscribe to Newsletter",
|
||||
"letsGoButtonText": "Let's Go",
|
||||
"letsGoButtonText": "Quick Start",
|
||||
"title": "Title",
|
||||
"signUp": {
|
||||
"buttonText": "Sign Up",
|
||||
@@ -40,6 +40,12 @@
|
||||
"markdown": "Markdown",
|
||||
"copyLink": "Copy Link"
|
||||
},
|
||||
"moreAction": {
|
||||
"small": "small",
|
||||
"medium": "medium",
|
||||
"large": "large",
|
||||
"fontSize": "Font Size"
|
||||
},
|
||||
"disclosureAction": {
|
||||
"rename": "Rename",
|
||||
"delete": "Delete",
|
||||
@@ -90,14 +96,17 @@
|
||||
"inlineCode": "Inline Code",
|
||||
"quote": "Quote Block",
|
||||
"header": "Header",
|
||||
"highlight": "Highlight"
|
||||
"highlight": "Highlight",
|
||||
"color": "Color"
|
||||
},
|
||||
"tooltip": {
|
||||
"lightMode": "Switch to Light mode",
|
||||
"darkMode": "Switch to Dark mode",
|
||||
"openAsPage": "Open as a Page",
|
||||
"addNewRow": "Add a new row",
|
||||
"openMenu": "Click to open menu"
|
||||
"openMenu": "Click to open menu",
|
||||
"viewDataBase": "View database",
|
||||
"referencePage": "This {name} is referenced"
|
||||
},
|
||||
"sideBar": {
|
||||
"closeSidebar": "Close side bar",
|
||||
@@ -149,16 +158,44 @@
|
||||
"appearance": "Appearance",
|
||||
"language": "Language",
|
||||
"user": "User",
|
||||
"files": "Files",
|
||||
"open": "Open Settings"
|
||||
},
|
||||
"appearance": {
|
||||
"lightLabel": "Light Mode",
|
||||
"darkLabel": "Dark Mode"
|
||||
"themeMode": {
|
||||
"label": "Theme Mode",
|
||||
"light": "Light Mode",
|
||||
"dark": "Dark Mode",
|
||||
"system": "Adapt to System"
|
||||
},
|
||||
"theme": "Theme"
|
||||
},
|
||||
"files": {
|
||||
"defaultLocation": "Where your data is stored now",
|
||||
"doubleTapToCopy": "Double tap to copy the path",
|
||||
"restoreLocation": "Restore to AppFlowy default path",
|
||||
"customizeLocation": "Open another folder",
|
||||
"restartApp": "Please restart app for the changes to take effect.",
|
||||
"exportDatabase": "Export database",
|
||||
"selectFiles": "Select the files that need to be export",
|
||||
"createNewFolder": "Create a new folder",
|
||||
"createNewFolderDesc": "Tell us where you want to store your data ...",
|
||||
"open": "Open",
|
||||
"openFolder": "Open an existing folder",
|
||||
"openFolderDesc": "Read and write it to your existing AppFlowy folder ...",
|
||||
"folderHintText": "folder name",
|
||||
"location": "Creating a new folder",
|
||||
"locationDesc": "Pick a name for your AppFlowy data folder",
|
||||
"browser": "Browse",
|
||||
"create": "Create",
|
||||
"folderPath": "Path to store your folder",
|
||||
"locationCannotBeEmpty": "Path cannot be empty"
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
"settings": {
|
||||
"filter": "Filter",
|
||||
"sort": "Sort",
|
||||
"sortBy": "Sort by",
|
||||
"Properties": "Properties",
|
||||
"group": "Group",
|
||||
@@ -191,6 +228,10 @@
|
||||
"is": "is"
|
||||
}
|
||||
},
|
||||
"checklistFilter": {
|
||||
"isComplete": "is complete",
|
||||
"isIncomplted": "is incomplete"
|
||||
},
|
||||
"singleSelectOptionFilter": {
|
||||
"is": "Is",
|
||||
"isNot": "Is not",
|
||||
@@ -216,14 +257,15 @@
|
||||
"singleSelectFieldName": "Select",
|
||||
"multiSelectFieldName": "Multiselect",
|
||||
"urlFieldName": "URL",
|
||||
"numberFormat": " Number format",
|
||||
"dateFormat": " Date format",
|
||||
"includeTime": " Include time",
|
||||
"checklistFieldName": "Checklist",
|
||||
"numberFormat": "Number format",
|
||||
"dateFormat": "Date format",
|
||||
"includeTime": "Include time",
|
||||
"dateFormatFriendly": "Month Day,Year",
|
||||
"dateFormatISO": "Year-Month-Day",
|
||||
"dateFormatLocal": "Month/Day/Year",
|
||||
"dateFormatUS": "Year/Month/Day",
|
||||
"timeFormat": " Time format",
|
||||
"timeFormat": "Time format",
|
||||
"invalidTimeFormat": "Invalid format",
|
||||
"timeFormatTwelveHour": "12 hour",
|
||||
"timeFormatTwentyFourHour": "24 hour",
|
||||
@@ -234,6 +276,12 @@
|
||||
"newColumn": "New column",
|
||||
"deleteFieldPromptMessage": "Are you sure? This property will be deleted"
|
||||
},
|
||||
"sort": {
|
||||
"ascending": "Ascending",
|
||||
"descending": "Descending",
|
||||
"deleteSort": "Delete sort",
|
||||
"addSort": "Add sort"
|
||||
},
|
||||
"row": {
|
||||
"duplicate": "Duplicate",
|
||||
"delete": "Delete",
|
||||
@@ -258,6 +306,9 @@
|
||||
"panelTitle": "Select an option or create one",
|
||||
"searchOption": "Search for an option"
|
||||
},
|
||||
"checklist": {
|
||||
"panelTitle": "Add an item"
|
||||
},
|
||||
"menuName": "Grid"
|
||||
},
|
||||
"document": {
|
||||
@@ -265,11 +316,33 @@
|
||||
"date": {
|
||||
"timeHintTextInTwelveHour": "01:00 PM",
|
||||
"timeHintTextInTwentyFourHour": "13:00"
|
||||
},
|
||||
"slashMenu": {
|
||||
"board": {
|
||||
"selectABoardToLinkTo": "Select a Board to link to"
|
||||
},
|
||||
"grid": {
|
||||
"selectAGridToLinkTo": "Select a Grid to link to"
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"referencedBoard": "Referenced Board",
|
||||
"referencedGrid": "Referenced Grid"
|
||||
}
|
||||
},
|
||||
"board": {
|
||||
"column": {
|
||||
"create_new_card": "New"
|
||||
},
|
||||
"menuName": "Board"
|
||||
},
|
||||
"calendar": {
|
||||
"menuName": "Calendar",
|
||||
"navigation": {
|
||||
"today": "Today",
|
||||
"jumpToday": "Jump to Today",
|
||||
"previousMonth": "Previous Month",
|
||||
"nextMonth": "Next Month"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,8 @@
|
||||
"inlineCode": "Código embebido",
|
||||
"quote": "Cita",
|
||||
"header": "Título",
|
||||
"highlight": "Resaltado"
|
||||
"highlight": "Resaltado",
|
||||
"color": "Color"
|
||||
},
|
||||
"tooltip": {
|
||||
"lightMode": "Cambiar a modo Claro",
|
||||
@@ -144,8 +145,12 @@
|
||||
"open": "Abrir ajustes"
|
||||
},
|
||||
"appearance": {
|
||||
"lightLabel": "Modo Claro",
|
||||
"darkLabel": "Modo Oscuro"
|
||||
"themeMode": {
|
||||
"label": "Theme Mode",
|
||||
"light": "Modo Claro",
|
||||
"dark": "Modo Oscuro",
|
||||
"system": "Adapt to System"
|
||||
}
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
@@ -218,9 +223,9 @@
|
||||
"openSidebar": "Abrir panel lateral",
|
||||
"closeSidebar": "Cerrar panel lateral"
|
||||
},
|
||||
"board": {
|
||||
"board": {
|
||||
"column": {
|
||||
"create_new_card": "Nuevo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
{
|
||||
"appName": "AppFlowy",
|
||||
"defaultUsername": "Ni",
|
||||
"welcomeText": "Ongietorri @:appName -ra",
|
||||
"githubStarText": "Izarra GitHub-en",
|
||||
"subscribeNewsletterText": "Harpidetu buletinera",
|
||||
"letsGoButtonText": "Hasi",
|
||||
"title": "Izenburua",
|
||||
"signUp": {
|
||||
"buttonText": "Izena eman",
|
||||
"title": "Izena eman @:appName -ra",
|
||||
"getStartedText": "Hasi",
|
||||
"emptyPasswordError": "Pasahitzak ezin du hutsik egon",
|
||||
"repeatPasswordEmptyError": "Pasahitz errepikapenak ezin du hutsik egon",
|
||||
"unmatchedPasswordError": "Pasahitz errepikapena ez da berdina",
|
||||
"alreadyHaveAnAccount": "Kontu bat duzu jada?",
|
||||
"emailHint": "Emaila",
|
||||
"passwordHint": "Pasahitza",
|
||||
"repeatPasswordHint": "Pasahitza errepikatu"
|
||||
},
|
||||
"signIn": {
|
||||
"loginTitle": "Hasi saioa @:appName -n",
|
||||
"loginButtonText": "Hasi saioa",
|
||||
"buttonText": "Sartu",
|
||||
"forgotPassword": "Pasahitza ahaztu duzu?",
|
||||
"emailHint": "Emaila",
|
||||
"passwordHint": "Pasahitza",
|
||||
"dontHaveAnAccount": "Ez daukazu konturik?",
|
||||
"repeatPasswordEmptyError": "Pasahitz errepikapenak ezin du hutsik egon",
|
||||
"unmatchedPasswordError": "Pasahitz errepikapena ez da berdina"
|
||||
},
|
||||
"workspace": {
|
||||
"create": "Lan-eremua",
|
||||
"hint": "lan-eremua",
|
||||
"notFoundError": "Lan-eremurik ez da aurkitu"
|
||||
},
|
||||
"shareAction": {
|
||||
"buttonText": "Konpartitu",
|
||||
"workInProgress": "Laister",
|
||||
"markdown": "Markdown",
|
||||
"copyLink": "Esteka kopiatu"
|
||||
},
|
||||
"moreAction": {
|
||||
"small": "txikia",
|
||||
"medium": "ertaina",
|
||||
"large": "handia",
|
||||
"fontSize": "Letra tamaina"
|
||||
},
|
||||
"disclosureAction": {
|
||||
"rename": "Izena aldatu",
|
||||
"delete": "Ezabatu",
|
||||
"duplicate": "Duplikatu"
|
||||
},
|
||||
"blankPageTitle": "Orri zuria",
|
||||
"newPageText": "Orri berria",
|
||||
"trash": {
|
||||
"text": "Zaborrontzia",
|
||||
"restoreAll": "Guztia berreskuratu",
|
||||
"deleteAll": "Guztia ezabatu",
|
||||
"pageHeader": {
|
||||
"fileName": "Fitxategi izena",
|
||||
"lastModified": "Azken aldaketa",
|
||||
"created": "Sortua"
|
||||
}
|
||||
},
|
||||
"deletePagePrompt": {
|
||||
"text": "Orri hau zaborrontzian dago",
|
||||
"restore": "Orria berreskuratu",
|
||||
"deletePermanent": "Betirako ezabatu"
|
||||
},
|
||||
"dialogCreatePageNameHint": "Orriaren izena",
|
||||
"questionBubble": {
|
||||
"whatsNew": "Ze berri?",
|
||||
"help": "Laguntza",
|
||||
"debug": {
|
||||
"name": "Debug informazioa",
|
||||
"success": "Debug informazioa kopiatu da!",
|
||||
"fail": "Ezin izan da debug informazioa kopiatu"
|
||||
}
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"addPageTooltip": "Gehitu orri bat",
|
||||
"defaultNewPageName": "Izenbururik ez",
|
||||
"renameDialog": "Izena aldatu"
|
||||
},
|
||||
"toolbar": {
|
||||
"undo": "Desegin",
|
||||
"redo": "Berregin",
|
||||
"bold": "Lodia",
|
||||
"italic": "Etzana",
|
||||
"underline": "Azpimarratua",
|
||||
"strike": "Markatua",
|
||||
"numList": "Zembakidun zerrenda",
|
||||
"bulletList": "Buletetako zerrenda",
|
||||
"checkList": "Egiaztapen zerrenda",
|
||||
"inlineCode": "Lerroko kodea",
|
||||
"quote": "Aipamena",
|
||||
"header": "Goiburua",
|
||||
"highlight": "Nabarmendu",
|
||||
"color": "Kolorea"
|
||||
},
|
||||
"tooltip": {
|
||||
"lightMode": "Modu argira aldatu",
|
||||
"darkMode": "Modu ilunera aldatu",
|
||||
"openAsPage": "Orri gisa ireki",
|
||||
"addNewRow": "Ilara berri bat gehitu",
|
||||
"openMenu": "Egin klik menua irekitzeko"
|
||||
},
|
||||
"sideBar": {
|
||||
"closeSidebar": "Alboko barra itxi",
|
||||
"openSidebar": "Alboko barra ireki"
|
||||
},
|
||||
"notifications": {
|
||||
"export": {
|
||||
"markdown": "Oharra markdownera esportatuta",
|
||||
"path": "Documents/flowy"
|
||||
}
|
||||
},
|
||||
"contactsPage": {
|
||||
"title": "Kontaktuak",
|
||||
"whatsHappening": "Ze berri aste honetan?",
|
||||
"addContact": "Kontaktua gehitu",
|
||||
"editContact": "Kontaktua editatu"
|
||||
},
|
||||
"button": {
|
||||
"OK": "OK",
|
||||
"Cancel": "Ezteztatu",
|
||||
"signIn": "Saioa hasi",
|
||||
"signOut": "Saioa itxi",
|
||||
"complete": "Burututa",
|
||||
"save": "Gorde"
|
||||
},
|
||||
"label": {
|
||||
"welcome": "Ongi etorri!",
|
||||
"firstName": "Izena",
|
||||
"middleName": "Bigarren izena",
|
||||
"lastName": "Abizena",
|
||||
"stepX": "{X}. pausoa"
|
||||
},
|
||||
"oAuth": {
|
||||
"err": {
|
||||
"failedTitle": "Ezin izan da kontura sartu.",
|
||||
"failedMsg": "Mesedez, ziurtatu zure arakatzailean saioa hasteko prozesua amaitu duzula."
|
||||
},
|
||||
"google": {
|
||||
"title": "GOOGLE SAIOA HASI",
|
||||
"instruction1": "Zure Google Kontaktuak inportatzeko, zure web arakatzailea erabiliz aplikazio hau baimendu beharko duzu.",
|
||||
"instruction2": "Kopiatu kode hau ikonoan klik eginez edo testua hautatuz:",
|
||||
"instruction3": "Nabigatu zure web arakatzailean esteka honetara eta idatzi goiko kodea:",
|
||||
"instruction4": "Sakatu beheko botoia erregistroa amaitzean:"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"title": "Ezarpenak",
|
||||
"menu": {
|
||||
"appearance": "Itxura",
|
||||
"language": "Hizkuntza",
|
||||
"user": "Erabiltzailea",
|
||||
"files": "Fitxategiak",
|
||||
"open": "Ezarpenak ireki"
|
||||
},
|
||||
"appearance": {
|
||||
"themeMode": {
|
||||
"label": "Itxura modua",
|
||||
"light": "Modu argia",
|
||||
"dark": "Modu iluna",
|
||||
"system": "Zure sistemara moldatu"
|
||||
},
|
||||
"theme": "Itxura"
|
||||
},
|
||||
"files": {
|
||||
"defaultLocation": "Non gordetzen diren zure datuak",
|
||||
"doubleTapToCopy": "Sakatu birritan bidea kopiatzeko",
|
||||
"restoreLocation": "Berrezarri AppFlowy-ren biden lehenetsira",
|
||||
"customizeLocation": "Beste karpeta bat ireki",
|
||||
"restartApp": "Mesedez, berrabiarazi aplikazioa aldaketak indarrean egon daitezen.",
|
||||
"exportDatabase": "Datubasea exportatu",
|
||||
"selectFiles": "Aukeratu exportatu nahi dituzun fitxategiak",
|
||||
"createNewFolder": "Karpeta berri bat sortu",
|
||||
"createNewFolderDesc": "Non nahi dituzu datuak gorde ...",
|
||||
"open": "Oreki",
|
||||
"openFolder": "Ireki karpeta bat",
|
||||
"openFolderDesc": "Irakurri eta idatzi zure AppFlowy karpetan...",
|
||||
"folderHintText": "karpetaren izena",
|
||||
"location": "Karpeta berria sortzen",
|
||||
"locationDesc": "Aukeratu izen bat AppFlowy datuen karpetarako",
|
||||
"browser": "Bilatu",
|
||||
"create": "Sortu",
|
||||
"folderPath": "Zure karpeta gordetzeko bidea",
|
||||
"locationCannotBeEmpty": "Bideak ezin du hutsa egon"
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
"settings": {
|
||||
"filter": "Filtroa",
|
||||
"sort": "Ordenatu",
|
||||
"sortBy": "Ordenatu honekiko",
|
||||
"Properties": "Propietateak",
|
||||
"group": "Taldea",
|
||||
"addFilter": "Gehitu iragazkia",
|
||||
"deleteFilter": "Ezabatu iragazkia",
|
||||
"filterBy": "Iragazi arabera...",
|
||||
"typeAValue": "Idatzi balio bat..."
|
||||
},
|
||||
"textFilter": {
|
||||
"contains": "Dauka",
|
||||
"doesNotContain": "Ez dauka",
|
||||
"endsWith": "Honez amaitzen da",
|
||||
"startWith": "Honez hasten da",
|
||||
"is": "da",
|
||||
"isNot": "Ez da",
|
||||
"isEmpty": "Hutsa dago",
|
||||
"isNotEmpty": "Ez dago hutsik",
|
||||
"choicechipPrefix": {
|
||||
"isNot": "Ez da",
|
||||
"startWith": "Honez hasten da",
|
||||
"endWith": "Honez amaitzen da",
|
||||
"isEmpty": "hutsik dago",
|
||||
"isNotEmpty": "ez dago hutsik"
|
||||
}
|
||||
},
|
||||
"checkboxFilter": {
|
||||
"isChecked": "Egiaztatuta",
|
||||
"isUnchecked": "Desmarkatua",
|
||||
"choicechipPrefix": {
|
||||
"da": "da"
|
||||
}
|
||||
},
|
||||
"checklistFilter": {
|
||||
"isComplete": "osatu da",
|
||||
"isIncomplted": "osatu gabe dago"
|
||||
},
|
||||
"singleSelectOptionFilter": {
|
||||
"is": "da",
|
||||
"isNot": "Ez da",
|
||||
"isEmpty": "Hutsa dago",
|
||||
"isNotEmpty": "Ez dago hutsik"
|
||||
},
|
||||
"multiSelectOptionFilter": {
|
||||
"contains": "Duen",
|
||||
"doesNotContain": "Ez dauka",
|
||||
"isEmpty": "Hutsa dago",
|
||||
"isNotEmpty": "Ez dago hutsik"
|
||||
},
|
||||
"field": {
|
||||
"hide": "Ezkutatu",
|
||||
"insertLeft": "Txertatu ezkerrera",
|
||||
"insertRight": "Txertatu eskuinera",
|
||||
"duplicate": "Bikoiztu",
|
||||
"delete": "Ezabatu",
|
||||
"textFieldName": "Testua",
|
||||
"checkboxFieldName": "Markatu laukia",
|
||||
"dateFieldName": "Data",
|
||||
"numberFieldName": "Zenbakiak",
|
||||
"singleSelectFieldName": "Hautatu",
|
||||
"multiSelectFieldName": "Multi-hautaketa",
|
||||
"urlFieldName": "URL",
|
||||
"checklistFieldName": "Kontrol zerrenda",
|
||||
"numberFormat": "Zenbaki formatua",
|
||||
"dateFormat": "Data formatua",
|
||||
"includeTime": "Sartu ordua",
|
||||
"dateFormatFriendly": "Hilabete Eguna, Urtea",
|
||||
"dateFormatISO": "Urtea-Hilabetea-Eguna",
|
||||
"dateFormatLocal": "Hilabetea/Eguna/Urtea",
|
||||
"dateFormatUS": "Urtea/Hilabetea/Eguna",
|
||||
"timeFormat": "Denboraren formatua",
|
||||
"invalidTimeFormat": "Formatu baliogabea",
|
||||
"timeFormatTwelveHour": "12 ordu",
|
||||
"timeFormatTwentyFourHour": "24 ordu",
|
||||
"addSelectOption": "Gehitu aukera bat",
|
||||
"optionTitle": "Aukerak",
|
||||
"addOption": "Gehitu aukera",
|
||||
"editProperty": "Editatu propietatea",
|
||||
"newColumn": "Zutabe berria",
|
||||
"deleteFieldPromptMessage": "Ziur al zaude? Propietate hau ezabatu egingo da"
|
||||
},
|
||||
"sort": {
|
||||
"ascending": "Gorarantz",
|
||||
"descending": "Jaisten",
|
||||
"deleteSort": "Ezabatu ordena",
|
||||
"addSort": "Gehitu ordenatu"
|
||||
},
|
||||
"row": {
|
||||
"duplicate": "Bikoiztu",
|
||||
"delete": "Ezabatu",
|
||||
"textPlaceholder": "Hutsik",
|
||||
"copyProperty": "Propietatea arbelean kopiatu da",
|
||||
"count": "Kontatu",
|
||||
"newRow": "Errenkada berria"
|
||||
},
|
||||
"selectOption": {
|
||||
"create": "Sortu",
|
||||
"purpleColor": "Purple",
|
||||
"pinkColor": "Rosa",
|
||||
"lightPinkColor": "Arrosa argia",
|
||||
"orangeColor": "Laranja",
|
||||
"yellowColor": "Horia",
|
||||
"limeColor": "Lima",
|
||||
"greenColor": "Berdea",
|
||||
"aquaColor": "Aqua",
|
||||
"blueColor": "Urdina",
|
||||
"deleteTag": "Ezabatu etiketa",
|
||||
"colorPanelTitle": "Koloreak",
|
||||
"panelTitle": "Hautatu aukera bat edo sortu bat",
|
||||
"searchOption": "Aukera bat bilatu"
|
||||
},
|
||||
"checklist": {
|
||||
"panelTitle": "Gehitu elementu bat"
|
||||
},
|
||||
"menuName": "Sareta"
|
||||
},
|
||||
"document": {
|
||||
"menuName": "Dokumentua",
|
||||
"data": {
|
||||
"timeHintTextInTwelveHour": "01:00 PM",
|
||||
"timeHintTextInTwentyFourHour": "13:00"
|
||||
}
|
||||
},
|
||||
"board": {
|
||||
"column": {
|
||||
"create_new_card": "Berria"
|
||||
}
|
||||
},
|
||||
"calendar": {
|
||||
"menuName": "Egutegia",
|
||||
"navigation": {
|
||||
"today": "Gaur",
|
||||
"jumpToday": "Gaurko egunera salto egin",
|
||||
"previousMonth": "Aurreko hilabetea",
|
||||
"nextMonth": "Hurrengo hilabetea"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,12 +138,16 @@
|
||||
"open": "Ouvrir les paramètres"
|
||||
},
|
||||
"appearance": {
|
||||
"lightLabel": "Mode clair",
|
||||
"darkLabel": "Mode sombre"
|
||||
"themeMode": {
|
||||
"label": "Theme Mode",
|
||||
"light": "Mode clair",
|
||||
"dark": "Mode sombre",
|
||||
"system": "Adapt to System"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sideBar": {
|
||||
"openSidebar": "Open sidebar",
|
||||
"closeSidebar": "Close sidebar"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -152,8 +152,12 @@
|
||||
"open": "Ouvrir les paramètres"
|
||||
},
|
||||
"appearance": {
|
||||
"lightLabel": "Mode clair",
|
||||
"darkLabel": "Mode sombre"
|
||||
"themeMode": {
|
||||
"label": "Theme Mode",
|
||||
"light": "Mode clair",
|
||||
"dark": "Mode sombre",
|
||||
"system": "Adapt to System"
|
||||
}
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
@@ -176,14 +180,14 @@
|
||||
"singleSelectFieldName": "Sélectionner",
|
||||
"multiSelectFieldName": "Multisélection",
|
||||
"urlFieldName": "URL",
|
||||
"numberFormat": " Format du nombre",
|
||||
"dateFormat": " Format de la date",
|
||||
"includeTime": " Inclure l'heure",
|
||||
"numberFormat": "Format du nombre",
|
||||
"dateFormat": "Format de la date",
|
||||
"includeTime": "Inclure l'heure",
|
||||
"dateFormatFriendly": "Mois Jour, Année",
|
||||
"dateFormatISO": "Année-Mois-Jour",
|
||||
"dateFormatLocal": "Mois/Jour/Année",
|
||||
"dateFormatUS": "Année/Mois/Jour",
|
||||
"timeFormat": " Format du temps",
|
||||
"timeFormat": "Format du temps",
|
||||
"invalidTimeFormat": "Format invalide",
|
||||
"timeFormatTwelveHour": "12 heures",
|
||||
"timeFormatTwentyFourHour": "24 heures",
|
||||
|
||||
@@ -138,12 +138,16 @@
|
||||
"open": "Beállítások megnyitása"
|
||||
},
|
||||
"appearance": {
|
||||
"lightLabel": "Világos mód",
|
||||
"darkLabel": "Éjjeli mód"
|
||||
"themeMode": {
|
||||
"label": "Theme Mode",
|
||||
"light": "Világos mód",
|
||||
"dark": "Éjjeli mód",
|
||||
"system": "Adapt to System"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sideBar": {
|
||||
"openSidebar": "Open sidebar",
|
||||
"closeSidebar": "Close sidebar"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,8 +145,12 @@
|
||||
"open": "Buka Pengaturan"
|
||||
},
|
||||
"appearance": {
|
||||
"lightLabel": "Mode Terang",
|
||||
"darkLabel": "Mode Gelap"
|
||||
"themeMode": {
|
||||
"label": "Theme Mode",
|
||||
"light": "Mode Terang",
|
||||
"dark": "Mode Gelap",
|
||||
"system": "Adapt to System"
|
||||
}
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
@@ -168,14 +172,14 @@
|
||||
"singleSelectFieldName": "seleksi",
|
||||
"multiSelectFieldName": "Multi seleksi",
|
||||
"urlFieldName": "URL",
|
||||
"numberFormat": " Format angka",
|
||||
"dateFormat": " Format tanggal",
|
||||
"includeTime": " Sertakan waktu",
|
||||
"numberFormat": "Format angka",
|
||||
"dateFormat": "Format tanggal",
|
||||
"includeTime": "Sertakan waktu",
|
||||
"dateFormatFriendly": "Bulan Hari,Tahun",
|
||||
"dateFormatISO": "Tahun-Bulan-Hari",
|
||||
"dateFormatLocal": "Bulan/Hari/Tahun",
|
||||
"dateFormatUS": "Tahun/Bulan/Hari",
|
||||
"timeFormat": " Format waktu",
|
||||
"timeFormat": "Format waktu",
|
||||
"invalidTimeFormat": "Format yang tidak valid",
|
||||
"timeFormatTwelveHour": "12 jam",
|
||||
"timeFormatTwentyFourHour": "24 jam",
|
||||
|
||||
@@ -138,18 +138,22 @@
|
||||
"open": "aprire le impostazioni"
|
||||
},
|
||||
"appearance": {
|
||||
"lightLabel": "Modalità Chiara",
|
||||
"darkLabel": "Modalità Scura"
|
||||
"themeMode": {
|
||||
"label": "Theme Mode",
|
||||
"light": "Modalità Chiara",
|
||||
"dark": "Modalità Scura",
|
||||
"system": "Adapt to System"
|
||||
}
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
"menuName":"Griglia"
|
||||
"menuName": "Griglia"
|
||||
},
|
||||
"document":{
|
||||
"menuName":"Documento"
|
||||
"document": {
|
||||
"menuName": "Documento"
|
||||
},
|
||||
"sideBar": {
|
||||
"openSidebar": "Open sidebar",
|
||||
"closeSidebar": "Close sidebar"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,8 +138,12 @@
|
||||
"open": "設定"
|
||||
},
|
||||
"appearance": {
|
||||
"lightLabel": "ライトモード",
|
||||
"darkLabel": "ダークモード"
|
||||
"themeMode": {
|
||||
"label": "Theme Mode",
|
||||
"light": "ライトモード",
|
||||
"dark": "ダークモード",
|
||||
"system": "Adapt to System"
|
||||
}
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
@@ -160,14 +164,14 @@
|
||||
"numberFieldName": "数値",
|
||||
"singleSelectFieldName": "単一選択",
|
||||
"multiSelectFieldName": "複数選択",
|
||||
"numberFormat": " 数値書式",
|
||||
"dateFormat": " 日付書式",
|
||||
"includeTime": " 時刻を含める",
|
||||
"numberFormat": "数値書式",
|
||||
"dateFormat": "日付書式",
|
||||
"includeTime": "時刻を含める",
|
||||
"dateFormatFriendly": "月 日,年",
|
||||
"dateFormatISO": "年-月-日",
|
||||
"dateFormatLocal": "月/日/年",
|
||||
"dateFormatUS": "年/月/日",
|
||||
"timeFormat": " 時刻書式",
|
||||
"timeFormat": "時刻書式",
|
||||
"timeFormatTwelveHour": "12 時間表記",
|
||||
"timeFormatTwentyFourHour": "24 時間表記",
|
||||
"addSelectOption": "選択候補追加",
|
||||
|
||||
@@ -1,236 +1,235 @@
|
||||
{
|
||||
"appName": "AppFlowy",
|
||||
"defaultUsername": "Me",
|
||||
"welcomeText": "@:appName 에 오신것을 환영합니다",
|
||||
"githubStarText": "Star on GitHub",
|
||||
"subscribeNewsletterText": "뉴스레터 구독",
|
||||
"letsGoButtonText": "Let's Go",
|
||||
"title": "제목",
|
||||
"signUp": {
|
||||
"buttonText": "회원가입",
|
||||
"title": "@:appName 에 회원가입",
|
||||
"getStartedText": "시작하기",
|
||||
"emptyPasswordError": "비밀번호는 공백일 수 없습니다",
|
||||
"repeatPasswordEmptyError": "비밀번호 재입력란은 공백일 수 없습니다",
|
||||
"unmatchedPasswordError": "재입력 하신 비밀번호가 같지 않습니다",
|
||||
"alreadyHaveAnAccount": "이미 계정이 있으신가요?",
|
||||
"emailHint": "이메일",
|
||||
"passwordHint": "비밀번호",
|
||||
"repeatPasswordHint": "비밀번호 재입력"
|
||||
"appName": "AppFlowy",
|
||||
"defaultUsername": "Me",
|
||||
"welcomeText": "@: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": "마크다운",
|
||||
"copyLink": "링크 복사"
|
||||
},
|
||||
"disclosureAction": {
|
||||
"rename": "이름변경",
|
||||
"delete": "삭제",
|
||||
"duplicate": "복제"
|
||||
},
|
||||
"blankPageTitle": "빈 페이지",
|
||||
"newPageText": "새로운 페이지",
|
||||
"trash": {
|
||||
"text": "휴지통",
|
||||
"restoreAll": "모두 복구",
|
||||
"deleteAll": "모두 삭제",
|
||||
"pageHeader": {
|
||||
"fileName": "파일 이름",
|
||||
"lastModified": "수정날짜",
|
||||
"created": "생성날짜"
|
||||
}
|
||||
},
|
||||
"deletePagePrompt": {
|
||||
"text": "현재 페이지는 휴지통에 있습니다",
|
||||
"restore": "페이지 복구",
|
||||
"deletePermanent": "영구 삭제"
|
||||
},
|
||||
"dialogCreatePageNameHint": "페이지 이름",
|
||||
"questionBubble": {
|
||||
"whatsNew": "새로운 소식",
|
||||
"help": "도움 및 지원",
|
||||
"debug": {
|
||||
"name": "디버그 정보",
|
||||
"success": "디버그 정보를 클립보드로 복사했습니다.",
|
||||
"fail": "디버그 정보를 클립보드로 복사할 수 없습니다."
|
||||
}
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"addPageTooltip": "하위에 페이지 추가",
|
||||
"defaultNewPageName": "제목없음",
|
||||
"renameDialog": "이름변경"
|
||||
},
|
||||
"toolbar": {
|
||||
"undo": "실행취소",
|
||||
"redo": "재실행",
|
||||
"bold": "굵게",
|
||||
"italic": "기울임꼴",
|
||||
"underline": "밑줄",
|
||||
"strike": "취소선",
|
||||
"numList": "번호 매기기 목록",
|
||||
"bulletList": "글머리 기호 목록",
|
||||
"checkList": "작업 목록",
|
||||
"inlineCode": "인라인 코드",
|
||||
"quote": "인용구 블록",
|
||||
"header": "헤더",
|
||||
"highlight": "하이라이트"
|
||||
},
|
||||
"tooltip": {
|
||||
"lightMode": "라이트 모드로 변경",
|
||||
"darkMode": "다크 모드로 변경",
|
||||
"openAsPage": "페이지로 열기",
|
||||
"addNewRow": "열 추가",
|
||||
"openMenu": "메뉴를 여시려면 클릭하세요"
|
||||
},
|
||||
"sideBar": {
|
||||
"closeSidebar": "사이드바 닫기",
|
||||
"openSidebar": "사이드바 열기"
|
||||
},
|
||||
"notifications": {
|
||||
"export": {
|
||||
"markdown": "마크다운으로 노트를 내보냄",
|
||||
"path": "Documents/flowy"
|
||||
}
|
||||
},
|
||||
"contactsPage": {
|
||||
"title": "연락처",
|
||||
"whatsHappening": "이번주에는 무슨 일이 있나요?",
|
||||
"addContact": "연락처 추가",
|
||||
"editContact": "연락처 편집"
|
||||
},
|
||||
"button": {
|
||||
"OK": "확인",
|
||||
"Cancel": "취소",
|
||||
"signIn": "로그인",
|
||||
"signOut": "로그아웃",
|
||||
"complete": "완료",
|
||||
"save": "저장"
|
||||
},
|
||||
"label": {
|
||||
"welcome": "환영합니다!",
|
||||
"firstName": "이름",
|
||||
"middleName": "중간 이름",
|
||||
"lastName": "성",
|
||||
"stepX": "{X} 단계"
|
||||
},
|
||||
"oAuth": {
|
||||
"err": {
|
||||
"failedTitle": "계정에 연결을 할 수 없습니다.",
|
||||
"failedMsg": "브라우저에서 회원가입이 완료되었는지 확인해주세요."
|
||||
},
|
||||
"signIn": {
|
||||
"loginTitle": "@:appName 에 로그인",
|
||||
"loginButtonText": "로그인",
|
||||
"buttonText": "로그인",
|
||||
"forgotPassword": "비밀번호를 잊으셨나요?",
|
||||
"emailHint": "이메일",
|
||||
"passwordHint": "비밀번호",
|
||||
"dontHaveAnAccount": "계정이 없으신가요?",
|
||||
"repeatPasswordEmptyError": "비밀번호 재입력란은 공백일 수 없습니다",
|
||||
"unmatchedPasswordError": "재입력 하신 비밀번호가 같지 않습니다"
|
||||
},
|
||||
"workspace": {
|
||||
"create": "워크스페이스 생성",
|
||||
"hint": "워크스페이스",
|
||||
"notFoundError": "워크스페이스를 찾을 수 없습니다"
|
||||
},
|
||||
"shareAction": {
|
||||
"buttonText": "공유",
|
||||
"workInProgress": "Coming soon",
|
||||
"markdown": "마크다운",
|
||||
"copyLink": "링크 복사"
|
||||
},
|
||||
"disclosureAction": {
|
||||
"rename": "이름변경",
|
||||
"delete": "삭제",
|
||||
"duplicate": "복제"
|
||||
},
|
||||
"blankPageTitle": "빈 페이지",
|
||||
"newPageText": "새로운 페이지",
|
||||
"trash": {
|
||||
"text": "휴지통",
|
||||
"restoreAll": "모두 복구",
|
||||
"deleteAll": "모두 삭제",
|
||||
"pageHeader": {
|
||||
"fileName": "파일 이름",
|
||||
"lastModified": "수정날짜",
|
||||
"created": "생성날짜"
|
||||
}
|
||||
},
|
||||
"deletePagePrompt": {
|
||||
"text": "현재 페이지는 휴지통에 있습니다",
|
||||
"restore": "페이지 복구",
|
||||
"deletePermanent": "영구 삭제"
|
||||
},
|
||||
"dialogCreatePageNameHint": "페이지 이름",
|
||||
"questionBubble": {
|
||||
"whatsNew": "새로운 소식",
|
||||
"help": "도움 및 지원",
|
||||
"debug": {
|
||||
"name": "디버그 정보",
|
||||
"success": "디버그 정보를 클립보드로 복사했습니다.",
|
||||
"fail": "디버그 정보를 클립보드로 복사할 수 없습니다."
|
||||
}
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"addPageTooltip": "하위에 페이지 추가",
|
||||
"defaultNewPageName": "제목없음",
|
||||
"renameDialog": "이름변경"
|
||||
},
|
||||
"toolbar": {
|
||||
"undo": "실행취소",
|
||||
"redo": "재실행",
|
||||
"bold": "굵게",
|
||||
"italic": "기울임꼴",
|
||||
"underline": "밑줄",
|
||||
"strike": "취소선",
|
||||
"numList": "번호 매기기 목록",
|
||||
"bulletList": "글머리 기호 목록",
|
||||
"checkList": "작업 목록",
|
||||
"inlineCode": "인라인 코드",
|
||||
"quote": "인용구 블록",
|
||||
"header": "헤더",
|
||||
"highlight": "하이라이트"
|
||||
},
|
||||
"tooltip": {
|
||||
"lightMode": "라이트 모드로 변경",
|
||||
"darkMode": "다크 모드로 변경",
|
||||
"openAsPage": "페이지로 열기",
|
||||
"addNewRow": "열 추가",
|
||||
"openMenu": "메뉴를 여시려면 클릭하세요"
|
||||
},
|
||||
"sideBar": {
|
||||
"closeSidebar": "사이드바 닫기",
|
||||
"openSidebar": "사이드바 열기"
|
||||
},
|
||||
"notifications": {
|
||||
"export": {
|
||||
"markdown": "마크다운으로 노트를 내보냄",
|
||||
"path": "Documents/flowy"
|
||||
}
|
||||
},
|
||||
"contactsPage": {
|
||||
"title": "연락처",
|
||||
"whatsHappening": "이번주에는 무슨 일이 있나요?",
|
||||
"addContact": "연락처 추가",
|
||||
"editContact": "연락처 편집"
|
||||
},
|
||||
"button": {
|
||||
"OK": "확인",
|
||||
"Cancel": "취소",
|
||||
"signIn": "로그인",
|
||||
"signOut": "로그아웃",
|
||||
"complete": "완료",
|
||||
"save": "저장"
|
||||
},
|
||||
"label": {
|
||||
"welcome": "환영합니다!",
|
||||
"firstName": "이름",
|
||||
"middleName": "중간 이름",
|
||||
"lastName": "성",
|
||||
"stepX": "{X} 단계"
|
||||
},
|
||||
"oAuth": {
|
||||
"err": {
|
||||
"failedTitle": "계정에 연결을 할 수 없습니다.",
|
||||
"failedMsg": "브라우저에서 회원가입이 완료되었는지 확인해주세요."
|
||||
},
|
||||
"google": {
|
||||
"title": "GOOGLE SIGN-IN",
|
||||
"instruction1": "구글 연락처를 가져오기 위해서 웹브라우저로 앱을 승인 해야 합니다.",
|
||||
"instruction2": "아이콘을 클릭 또는 텍스트를 선택해서 이 코드를 클립보드로 복사하세요:",
|
||||
"instruction3": "웹브라우저로 다음 링크로 가셔서 위 코드를 입력해주세요:",
|
||||
"instruction4": "가입 완료 후 아래 버튼을 눌러주세요:"
|
||||
}
|
||||
"google": {
|
||||
"title": "GOOGLE SIGN-IN",
|
||||
"instruction1": "구글 연락처를 가져오기 위해서 웹브라우저로 앱을 승인 해야 합니다.",
|
||||
"instruction2": "아이콘을 클릭 또는 텍스트를 선택해서 이 코드를 클립보드로 복사하세요:",
|
||||
"instruction3": "웹브라우저로 다음 링크로 가셔서 위 코드를 입력해주세요:",
|
||||
"instruction4": "가입 완료 후 아래 버튼을 눌러주세요:"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"title": "설정",
|
||||
"menu": {
|
||||
"appearance": "화면",
|
||||
"language": "언어",
|
||||
"user": "사용자",
|
||||
"open": "설정 열기"
|
||||
},
|
||||
"appearance": {
|
||||
"lightLabel": "라이트 모드",
|
||||
"darkLabel": "다크 모드"
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
"settings": {
|
||||
"title": "설정",
|
||||
"menu": {
|
||||
"appearance": "화면",
|
||||
"language": "언어",
|
||||
"user": "사용자",
|
||||
"open": "설정 열기"
|
||||
},
|
||||
"appearance": {
|
||||
"lightLabel": "라이트 모드",
|
||||
"darkLabel": "다크 모드"
|
||||
}
|
||||
"filter": "필터",
|
||||
"sortBy": "정렬 기준",
|
||||
"Properties": "속성",
|
||||
"group": "그룹"
|
||||
},
|
||||
"grid": {
|
||||
"settings": {
|
||||
"filter": "필터",
|
||||
"sortBy": "정렬 기준",
|
||||
"Properties": "속성",
|
||||
"group": "그룹"
|
||||
},
|
||||
"field": {
|
||||
"hide": "숨기기",
|
||||
"insertLeft": "왼쪽 삽입",
|
||||
"insertRight": "오른쪽 삽입",
|
||||
"duplicate": "복제",
|
||||
"delete": "삭제",
|
||||
"textFieldName": "텍스트",
|
||||
"checkboxFieldName": "체크박스",
|
||||
"dateFieldName": "날짜",
|
||||
"numberFieldName": "숫자",
|
||||
"singleSelectFieldName": "선택",
|
||||
"multiSelectFieldName": "다중선택",
|
||||
"urlFieldName": "링크",
|
||||
"numberFormat": " 숫자 형식",
|
||||
"dateFormat": " 날짜 형식",
|
||||
"includeTime": " 시간 표시",
|
||||
"dateFormatFriendly": "월 일,년",
|
||||
"dateFormatISO": "년-월-일",
|
||||
"dateFormatLocal": "월/일/년",
|
||||
"dateFormatUS": "년/월/일",
|
||||
"timeFormat": " 시간 형식",
|
||||
"invalidTimeFormat": "잘못된 형식",
|
||||
"timeFormatTwelveHour": "12 시간",
|
||||
"timeFormatTwentyFourHour": "24 시간",
|
||||
"addSelectOption": "옵션 추가",
|
||||
"optionTitle": "옵션",
|
||||
"addOption": "옵션 추가",
|
||||
"editProperty": "속성 편집",
|
||||
"newColumn": "열 추가",
|
||||
"deleteFieldPromptMessage": "해당 속성을 삭제 하시겠습니까?"
|
||||
},
|
||||
"row": {
|
||||
"duplicate": "복제",
|
||||
"delete": "삭제",
|
||||
"textPlaceholder": "비어있음",
|
||||
"copyProperty": "속성이 클립보드로 복사됨",
|
||||
"count": "개수",
|
||||
"newRow": "행 추가"
|
||||
},
|
||||
"selectOption": {
|
||||
"create": "생성",
|
||||
"purpleColor": "보라색",
|
||||
"pinkColor": "핑크색",
|
||||
"lightPinkColor": "연한 핑크색",
|
||||
"orangeColor": "오렌지색",
|
||||
"yellowColor": "노랑색",
|
||||
"limeColor": "라임색",
|
||||
"greenColor": "초록색",
|
||||
"aquaColor": "아쿠아색",
|
||||
"blueColor": "파랑색",
|
||||
"deleteTag": "태그 삭제",
|
||||
"colorPanelTitle": "색상",
|
||||
"panelTitle": "옵션 선택 또는 생성",
|
||||
"searchOption": "옵션 검색"
|
||||
},
|
||||
"menuName": "그리드"
|
||||
"field": {
|
||||
"hide": "숨기기",
|
||||
"insertLeft": "왼쪽 삽입",
|
||||
"insertRight": "오른쪽 삽입",
|
||||
"duplicate": "복제",
|
||||
"delete": "삭제",
|
||||
"textFieldName": "텍스트",
|
||||
"checkboxFieldName": "체크박스",
|
||||
"dateFieldName": "날짜",
|
||||
"numberFieldName": "숫자",
|
||||
"singleSelectFieldName": "선택",
|
||||
"multiSelectFieldName": "다중선택",
|
||||
"urlFieldName": "링크",
|
||||
"numberFormat": "숫자 형식",
|
||||
"dateFormat": "날짜 형식",
|
||||
"includeTime": "시간 표시",
|
||||
"dateFormatFriendly": "월 일,년",
|
||||
"dateFormatISO": "년-월-일",
|
||||
"dateFormatLocal": "월/일/년",
|
||||
"dateFormatUS": "년/월/일",
|
||||
"timeFormat": "시간 형식",
|
||||
"invalidTimeFormat": "잘못된 형식",
|
||||
"timeFormatTwelveHour": "12 시간",
|
||||
"timeFormatTwentyFourHour": "24 시간",
|
||||
"addSelectOption": "옵션 추가",
|
||||
"optionTitle": "옵션",
|
||||
"addOption": "옵션 추가",
|
||||
"editProperty": "속성 편집",
|
||||
"newColumn": "열 추가",
|
||||
"deleteFieldPromptMessage": "해당 속성을 삭제 하시겠습니까?"
|
||||
},
|
||||
"document": {
|
||||
"menuName": "도큐먼트",
|
||||
"date": {
|
||||
"timeHintTextInTwelveHour": "01:00 PM",
|
||||
"timeHintTextInTwentyFourHour": "13:00"
|
||||
}
|
||||
"row": {
|
||||
"duplicate": "복제",
|
||||
"delete": "삭제",
|
||||
"textPlaceholder": "비어있음",
|
||||
"copyProperty": "속성이 클립보드로 복사됨",
|
||||
"count": "개수",
|
||||
"newRow": "행 추가"
|
||||
},
|
||||
"board": {
|
||||
"column": {
|
||||
"create_new_card": "추가"
|
||||
}
|
||||
"selectOption": {
|
||||
"create": "생성",
|
||||
"purpleColor": "보라색",
|
||||
"pinkColor": "핑크색",
|
||||
"lightPinkColor": "연한 핑크색",
|
||||
"orangeColor": "오렌지색",
|
||||
"yellowColor": "노랑색",
|
||||
"limeColor": "라임색",
|
||||
"greenColor": "초록색",
|
||||
"aquaColor": "아쿠아색",
|
||||
"blueColor": "파랑색",
|
||||
"deleteTag": "태그 삭제",
|
||||
"colorPanelTitle": "색상",
|
||||
"panelTitle": "옵션 선택 또는 생성",
|
||||
"searchOption": "옵션 검색"
|
||||
},
|
||||
"menuName": "그리드"
|
||||
},
|
||||
"document": {
|
||||
"menuName": "도큐먼트",
|
||||
"date": {
|
||||
"timeHintTextInTwelveHour": "01:00 PM",
|
||||
"timeHintTextInTwentyFourHour": "13:00"
|
||||
}
|
||||
},
|
||||
"board": {
|
||||
"column": {
|
||||
"create_new_card": "추가"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -138,12 +138,16 @@
|
||||
"open": "Otwórz Ustawienia"
|
||||
},
|
||||
"appearance": {
|
||||
"lightLabel": "Tryb Jasny",
|
||||
"darkLabel": "Tryb Ciemny"
|
||||
"themeMode": {
|
||||
"label": "Theme Mode",
|
||||
"light": "Tryb Jasny",
|
||||
"dark": "Tryb Ciemny",
|
||||
"system": "Adapt to System"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sideBar": {
|
||||
"openSidebar": "Open sidebar",
|
||||
"closeSidebar": "Close sidebar"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -152,8 +152,12 @@
|
||||
"open": "Abrir as Configurações"
|
||||
},
|
||||
"appearance": {
|
||||
"lightLabel": "Modo Claro",
|
||||
"darkLabel": "Modo Escuro"
|
||||
"themeMode": {
|
||||
"label": "Theme Mode",
|
||||
"light": "Modo Claro",
|
||||
"dark": "Modo Escuro",
|
||||
"system": "Adapt to System"
|
||||
}
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
|
||||
@@ -40,6 +40,12 @@
|
||||
"markdown": "Markdown",
|
||||
"copyLink": "Скопировать ссылку"
|
||||
},
|
||||
"moreAction": {
|
||||
"small": "маленький",
|
||||
"medium": "средний",
|
||||
"large": "большой",
|
||||
"fontSize": "Размер шрифта"
|
||||
},
|
||||
"disclosureAction": {
|
||||
"rename": "Переименовать",
|
||||
"delete": "Удалить",
|
||||
@@ -62,7 +68,7 @@
|
||||
"restore": "Восстановить страницу",
|
||||
"deletePermanent": "Удалить навсегда"
|
||||
},
|
||||
"dialogCreatePageNameHint": "Имя",
|
||||
"dialogCreatePageNameHint": "Имя страницы",
|
||||
"questionBubble": {
|
||||
"whatsNew": "Что нового?",
|
||||
"help": "Помощь",
|
||||
@@ -90,10 +96,12 @@
|
||||
"inlineCode": "Код",
|
||||
"quote": "Цитата",
|
||||
"header": "Заголовок",
|
||||
"highlight": "Выделение"
|
||||
"highlight": "Выделение",
|
||||
"color": "Цвет"
|
||||
},
|
||||
"tooltip": {
|
||||
"darkMode": "Переключиться в тёмную тему",
|
||||
"lightMode": "Переключить на светлую тему",
|
||||
"darkMode": "Переключить на тёмную тему",
|
||||
"openAsPage": "Открыть как страницу",
|
||||
"addNewRow": "Добавить новую строку",
|
||||
"openMenu": "Открыть меню"
|
||||
@@ -148,19 +156,91 @@
|
||||
"appearance": "Внешний вид",
|
||||
"language": "Язык",
|
||||
"user": "Пользователь",
|
||||
"files": "Файлы",
|
||||
"open": "Открыть настройки"
|
||||
},
|
||||
"appearance": {
|
||||
"lightLabel": "Светлая",
|
||||
"darkLabel": "Тёмная"
|
||||
"themeMode": {
|
||||
"label": "Режим темы",
|
||||
"light": "Светлая",
|
||||
"dark": "Тёмная",
|
||||
"system": "Системная"
|
||||
},
|
||||
"theme":"Тема"
|
||||
},
|
||||
"files": {
|
||||
"defaultLocation": "Где хранить ваши данные",
|
||||
"doubleTapToCopy": "Нажмите дважды, чтобы скопировать путь",
|
||||
"restoreLocation": "Восстановить путь AppFlowy по умолчанию",
|
||||
"customizeLocation": "Открыть другую папку",
|
||||
"restartApp": "Пожалуйста, перезапустите приложение, чтобы изменения вступили в силу.",
|
||||
"exportDatabase": "Экспорт базы данных",
|
||||
"selectFiles": "Выбрать файлы, которые необходимо экспортировать",
|
||||
"createNewFolder": "Создать новую папку",
|
||||
"createNewFolderDesc": "Указать, где хранить свои данные ...",
|
||||
"open": "Открыть",
|
||||
"openFolder": "Открыть существующую папку",
|
||||
"openFolderDesc": "Чтение и запись в существующую папку AppFlowy ...",
|
||||
"folderHintText": "имя папки",
|
||||
"location": "Создание новой папки",
|
||||
"locationDesc": "Выбрать имя папки данных AppFlowy",
|
||||
"browser": "Обзор",
|
||||
"create": "Создать",
|
||||
"folderPath": "Путь к вашей папке",
|
||||
"locationCannotBeEmpty": "Путь не может быть пустым"
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
"settings": {
|
||||
"filter": "Фильтр",
|
||||
"sortBy": "Сортировать",
|
||||
"sort": "Сортировать",
|
||||
"sortBy": "Сортировать по",
|
||||
"Properties": "Свойства",
|
||||
"group": "Группировать"
|
||||
"group": "Группировать",
|
||||
"addFilter": "Добавить фильтр",
|
||||
"deleteFilter": "Удалить фильтр",
|
||||
"filterBy": "Фильтровать по...",
|
||||
"typeAValue": "Введите значение..."
|
||||
},
|
||||
"textFilter": {
|
||||
"contains": "Содержит",
|
||||
"doesNotContain": "Не содержит",
|
||||
"endsWith": "Заканчивается на",
|
||||
"startWith": "Начинается с",
|
||||
"is": "Является",
|
||||
"isNot": "Не является",
|
||||
"isEmpty": "Пусто",
|
||||
"isNotEmpty": "Не пусто",
|
||||
"choicechipPrefix": {
|
||||
"isNot": "Не является",
|
||||
"startWith": "Начинается с",
|
||||
"endWith": "Заканчивается на",
|
||||
"isEmpty": "пусто",
|
||||
"isNotEmpty": "не пусто"
|
||||
}
|
||||
},
|
||||
"checkboxFilter": {
|
||||
"isChecked": "Отмечено",
|
||||
"isUnchecked": "Не отмечено",
|
||||
"choicechipPrefix": {
|
||||
"is": "является"
|
||||
}
|
||||
},
|
||||
"checklistFilter": {
|
||||
"isComplete": "завершено",
|
||||
"isIncomplted": "не завершено"
|
||||
},
|
||||
"singleSelectOptionFilter": {
|
||||
"is": "Является",
|
||||
"isNot": "Не является",
|
||||
"isEmpty": "Пусто",
|
||||
"isNotEmpty": "Не пусто"
|
||||
},
|
||||
"multiSelectOptionFilter": {
|
||||
"contains": "Содержит",
|
||||
"doesNotContain": "Не содержит",
|
||||
"isEmpty": "Пусто",
|
||||
"isNotEmpty": "Не пусто"
|
||||
},
|
||||
"field": {
|
||||
"hide": "Скрыть",
|
||||
@@ -175,14 +255,15 @@
|
||||
"singleSelectFieldName": "Выбор",
|
||||
"multiSelectFieldName": "Выбор нескольких",
|
||||
"urlFieldName": "URL",
|
||||
"numberFormat": " Формат числа",
|
||||
"dateFormat": " Формат даты",
|
||||
"includeTime": " Время",
|
||||
"checklistFieldName": "Контрольный список",
|
||||
"numberFormat": "Формат числа",
|
||||
"dateFormat": "Формат даты",
|
||||
"includeTime": "Время",
|
||||
"dateFormatFriendly": "День Месяц, Год",
|
||||
"dateFormatISO": "Год-Месяц-День",
|
||||
"dateFormatLocal": "Месяц/День/Год",
|
||||
"dateFormatUS": "Год/Месяц/День",
|
||||
"timeFormat": " Форматировать время",
|
||||
"timeFormat": "Форматировать время",
|
||||
"invalidTimeFormat": "Неверный формат",
|
||||
"timeFormatTwelveHour": "12 часов",
|
||||
"timeFormatTwentyFourHour": "24 часа",
|
||||
@@ -193,12 +274,19 @@
|
||||
"newColumn": "Добавить колонку",
|
||||
"deleteFieldPromptMessage": "Вы уверены? Свойство будет удалено"
|
||||
},
|
||||
"sort": {
|
||||
"ascending": "По возрастанию",
|
||||
"descending": "По убыванию",
|
||||
"deleteSort": "Удалить сортировку",
|
||||
"addSort": "Добавить сортировку"
|
||||
},
|
||||
"row": {
|
||||
"duplicate": "Дублировать",
|
||||
"delete": "Удалить",
|
||||
"textPlaceholder": "Пусто",
|
||||
"copyProperty": "Свойство скопировано",
|
||||
"count": "Количество"
|
||||
"count": "Количество",
|
||||
"newRow": "Новая строка"
|
||||
},
|
||||
"selectOption": {
|
||||
"create": "Создать",
|
||||
@@ -216,6 +304,9 @@
|
||||
"panelTitle": "Выберите или создайте вариант",
|
||||
"searchOption": "Поиск"
|
||||
},
|
||||
"checklist": {
|
||||
"panelTitle": "Добавить элемент"
|
||||
},
|
||||
"menuName": "Сетка"
|
||||
},
|
||||
"document": {
|
||||
@@ -229,5 +320,8 @@
|
||||
"column": {
|
||||
"create_new_card": "Создать"
|
||||
}
|
||||
},
|
||||
"calendar": {
|
||||
"menuName": "Календарь"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -152,8 +152,12 @@
|
||||
"open": "Öppna inställningarna"
|
||||
},
|
||||
"appearance": {
|
||||
"lightLabel": "Ljust läge",
|
||||
"darkLabel": "Mörkt läge"
|
||||
"themeMode": {
|
||||
"label": "Theme Mode",
|
||||
"light": "Ljust läge",
|
||||
"dark": "Mörkt läge",
|
||||
"system": "Adapt to System"
|
||||
}
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
@@ -176,14 +180,14 @@
|
||||
"singleSelectFieldName": "Välj",
|
||||
"multiSelectFieldName": "Välj flera",
|
||||
"urlFieldName": "URL",
|
||||
"numberFormat": " Sifferformat",
|
||||
"dateFormat": " Datumformat",
|
||||
"includeTime": " Inkludera tid",
|
||||
"numberFormat": "Sifferformat",
|
||||
"dateFormat": "Datumformat",
|
||||
"includeTime": "Inkludera tid",
|
||||
"dateFormatFriendly": "Månad Dag,År",
|
||||
"dateFormatISO": "År-Månad-Dag",
|
||||
"dateFormatLocal": "Månad/Dag/År",
|
||||
"dateFormatUS": "År/Månad/Dag",
|
||||
"timeFormat": " Tidsformat",
|
||||
"timeFormat": "Tidsformat",
|
||||
"invalidTimeFormat": "Ogiltigt format",
|
||||
"timeFormatTwelveHour": "12-timmars",
|
||||
"timeFormatTwentyFourHour": "24-timmars",
|
||||
@@ -232,4 +236,4 @@
|
||||
"create_new_card": "Nytt"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,12 +138,16 @@
|
||||
"open": "Ayarları Aç"
|
||||
},
|
||||
"appearance": {
|
||||
"lightLabel": "Aydınlık Mod",
|
||||
"darkLabel": "Karanlık Mod"
|
||||
"themeMode": {
|
||||
"label": "Theme Mode",
|
||||
"light": "Aydınlık Mod",
|
||||
"dark": "Karanlık Mod",
|
||||
"system": "Adapt to System"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sideBar": {
|
||||
"openSidebar": "Open sidebar",
|
||||
"closeSidebar": "Close sidebar"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -152,8 +152,12 @@
|
||||
"open": "打开设置"
|
||||
},
|
||||
"appearance": {
|
||||
"lightLabel": "日间模式",
|
||||
"darkLabel": "夜间模式"
|
||||
"themeMode": {
|
||||
"label": "Theme Mode",
|
||||
"light": "日间模式",
|
||||
"dark": "夜间模式",
|
||||
"system": "Adapt to System"
|
||||
}
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
@@ -176,14 +180,14 @@
|
||||
"singleSelectFieldName": "单项选择器",
|
||||
"multiSelectFieldName": "多项选择器",
|
||||
"urlFieldName": "链接",
|
||||
"numberFormat": " 数字格式",
|
||||
"dateFormat": " 日期格式",
|
||||
"includeTime": " 包含时间",
|
||||
"numberFormat": "数字格式",
|
||||
"dateFormat": "日期格式",
|
||||
"includeTime": "包含时间",
|
||||
"dateFormatFriendly": "月 日,年",
|
||||
"dateFormatISO": "年-月-日",
|
||||
"dateFormatLocal": "月/日/年",
|
||||
"dateFormatUS": "年/月/日",
|
||||
"timeFormat": " 时间格式",
|
||||
"timeFormat": "时间格式",
|
||||
"invalidTimeFormat": "时间格式错误",
|
||||
"timeFormatTwelveHour": "12小时制",
|
||||
"timeFormatTwentyFourHour": "24小时制",
|
||||
|
||||
@@ -145,8 +145,12 @@
|
||||
"open": "開啟設定"
|
||||
},
|
||||
"appearance": {
|
||||
"lightLabel": "亮色模式",
|
||||
"darkLabel": "暗色模式"
|
||||
"themeMode": {
|
||||
"label": "Theme Mode",
|
||||
"light": "亮色模式",
|
||||
"dark": "暗色模式",
|
||||
"system": "Adapt to System"
|
||||
}
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
@@ -168,14 +172,14 @@
|
||||
"singleSelectFieldName": "單選",
|
||||
"multiSelectFieldName": "多選",
|
||||
"urlFieldName": "網址",
|
||||
"numberFormat": " 數字格式",
|
||||
"dateFormat": " 日期格式",
|
||||
"includeTime": " 包含時間",
|
||||
"numberFormat": "數字格式",
|
||||
"dateFormat": "日期格式",
|
||||
"includeTime": "包含時間",
|
||||
"dateFormatFriendly": "月 日,年",
|
||||
"dateFormatISO": "年-月-日",
|
||||
"dateFormatLocal": "月/日/年",
|
||||
"dateFormatUS": "年/月/日",
|
||||
"timeFormat": " 時間格式",
|
||||
"timeFormat": "時間格式",
|
||||
"invalidTimeFormat": "格式無效",
|
||||
"timeFormatTwelveHour": "12 小時",
|
||||
"timeFormatTwentyFourHour": "24 小時",
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
import 'package:app_flowy/user/presentation/folder/folder_widget.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text_field.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/mock/mock_file_picker.dart';
|
||||
import 'util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('customize the folder path', () {
|
||||
const location = 'appflowy';
|
||||
|
||||
setUp(() async {
|
||||
await TestFolder.cleanTestLocation(location);
|
||||
await TestFolder.setTestLocation(location);
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await TestFolder.cleanTestLocation(location);
|
||||
});
|
||||
|
||||
tearDownAll(() async {
|
||||
await TestFolder.cleanTestLocation(null);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'customize folder name and path when launching app in first time',
|
||||
(tester) async {
|
||||
const folderName = 'appflowy';
|
||||
await TestFolder.cleanTestLocation(folderName);
|
||||
|
||||
await tester.initializeAppFlowy();
|
||||
|
||||
// Click create button
|
||||
await tester.tapCreateButton();
|
||||
|
||||
// Set directory
|
||||
final cfw = find.byType(CreateFolderWidget);
|
||||
expect(cfw, findsOneWidget);
|
||||
final state = tester.state(cfw) as CreateFolderWidgetState;
|
||||
final dir = await TestFolder.testLocation(null);
|
||||
state.directory = dir.path;
|
||||
|
||||
// input folder name
|
||||
final ftf = find.byType(FlowyTextField);
|
||||
expect(ftf, findsOneWidget);
|
||||
await tester.enterText(ftf, 'appflowy');
|
||||
|
||||
// Click create button again
|
||||
await tester.tapCreateButton();
|
||||
|
||||
await tester.expectToSeeWelcomePage();
|
||||
|
||||
await TestFolder.cleanTestLocation(folderName);
|
||||
});
|
||||
|
||||
testWidgets('open a new folder when launching app in first time',
|
||||
(tester) async {
|
||||
const folderName = 'appflowy';
|
||||
await TestFolder.cleanTestLocation(folderName);
|
||||
await TestFolder.setTestLocation(folderName);
|
||||
|
||||
await tester.initializeAppFlowy();
|
||||
|
||||
// tap open button
|
||||
await mockGetDirectoryPath(folderName);
|
||||
await tester.tapOpenFolderButton();
|
||||
|
||||
await tester.wait(1000);
|
||||
await tester.expectToSeeWelcomePage();
|
||||
|
||||
await TestFolder.cleanTestLocation(folderName);
|
||||
});
|
||||
|
||||
testWidgets('switch to B from A, then switch to A again', (tester) async {
|
||||
const String userA = 'userA';
|
||||
const String userB = 'userB';
|
||||
|
||||
await TestFolder.cleanTestLocation(userA);
|
||||
await TestFolder.setTestLocation(userA);
|
||||
|
||||
await tester.initializeAppFlowy();
|
||||
|
||||
await tester.tapGoButton();
|
||||
await tester.expectToSeeWelcomePage();
|
||||
|
||||
// swith to user B
|
||||
{
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
await tester.enterUserName(userA);
|
||||
|
||||
await tester.openSettingsPage(SettingsPage.files);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// mock the file_picker result
|
||||
await mockGetDirectoryPath(userB);
|
||||
await tester.tapCustomLocationButton();
|
||||
await tester.pumpAndSettle();
|
||||
await tester.expectToSeeWelcomePage();
|
||||
}
|
||||
|
||||
// switch to the userA
|
||||
{
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
await tester.enterUserName(userB);
|
||||
|
||||
await tester.openSettingsPage(SettingsPage.files);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// mock the file_picker result
|
||||
await mockGetDirectoryPath(userA);
|
||||
await tester.tapCustomLocationButton();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
await tester.expectToSeeWelcomePage();
|
||||
expect(find.textContaining(userA), findsOneWidget);
|
||||
}
|
||||
|
||||
// swith to the userB again
|
||||
{
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.files);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// mock the file_picker result
|
||||
await mockGetDirectoryPath(userB);
|
||||
await tester.tapCustomLocationButton();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
await tester.expectToSeeWelcomePage();
|
||||
expect(find.textContaining(userB), findsOneWidget);
|
||||
}
|
||||
|
||||
await TestFolder.cleanTestLocation(userA);
|
||||
await TestFolder.cleanTestLocation(userB);
|
||||
});
|
||||
|
||||
testWidgets('reset to default location', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
|
||||
await tester.tapGoButton();
|
||||
|
||||
// home and readme document
|
||||
await tester.expectToSeeWelcomePage();
|
||||
|
||||
// open settings and restore the location
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.files);
|
||||
await tester.restoreLocation();
|
||||
|
||||
expect(
|
||||
await TestFolder.defaultDevelopmentLocation(),
|
||||
await TestFolder.currentLocation(),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:app_flowy/main.dart' as app;
|
||||
import 'package:app_flowy/startup/tasks/prelude.dart';
|
||||
import 'package:app_flowy/workspace/application/settings/settings_location_cubit.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class TestFolder {
|
||||
/// Location / Path
|
||||
|
||||
/// Set a given AppFlowy data storage location under test environment.
|
||||
///
|
||||
/// To pass null means clear the location.
|
||||
///
|
||||
/// The file_picker is a system component and can't be tapped, so using logic instead of tapping.
|
||||
///
|
||||
static Future<void> setTestLocation(String? name) async {
|
||||
final location = await testLocation(name);
|
||||
SharedPreferences.setMockInitialValues({
|
||||
kSettingsLocationDefaultLocation: location.path,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
/// Clean the location.
|
||||
static Future<void> cleanTestLocation(String? name) async {
|
||||
final dir = await testLocation(name);
|
||||
await dir.delete(recursive: true);
|
||||
return;
|
||||
}
|
||||
|
||||
/// Get current using location.
|
||||
static Future<String> currentLocation() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString(kSettingsLocationDefaultLocation)!;
|
||||
}
|
||||
|
||||
/// Get default location under development environment.
|
||||
static Future<String> defaultDevelopmentLocation() async {
|
||||
final dir = await appFlowyDocumentDirectory();
|
||||
return dir.path;
|
||||
}
|
||||
|
||||
/// Get default location under test environment.
|
||||
static Future<Directory> testLocation(String? name) async {
|
||||
final dir = await getApplicationDocumentsDirectory();
|
||||
var path = '${dir.path}/flowy_test';
|
||||
if (name != null) {
|
||||
path += '/$name';
|
||||
}
|
||||
return Directory(path).create(recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
extension AppFlowyTestBase on WidgetTester {
|
||||
Future<void> initializeAppFlowy() async {
|
||||
const MethodChannel('hotkey_manager')
|
||||
.setMockMethodCallHandler((MethodCall methodCall) async {
|
||||
if (methodCall.method == 'unregisterAll') {
|
||||
// do nothing
|
||||
}
|
||||
});
|
||||
|
||||
await app.main();
|
||||
await wait(3000);
|
||||
await pumpAndSettle(const Duration(seconds: 2));
|
||||
return;
|
||||
}
|
||||
|
||||
Future<void> tapButton(
|
||||
Finder finder, {
|
||||
int? pointer,
|
||||
int buttons = kPrimaryButton,
|
||||
bool warnIfMissed = true,
|
||||
int milliseconds = 500,
|
||||
}) async {
|
||||
await tap(finder);
|
||||
await pumpAndSettle(Duration(milliseconds: milliseconds));
|
||||
return;
|
||||
}
|
||||
|
||||
Future<void> tapButtonWithName(
|
||||
String tr, {
|
||||
int milliseconds = 500,
|
||||
}) async {
|
||||
final button = find.text(tr);
|
||||
await tapButton(
|
||||
button,
|
||||
milliseconds: milliseconds,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Future<void> tapButtonWithTooltip(
|
||||
String tr, {
|
||||
int milliseconds = 500,
|
||||
}) async {
|
||||
final button = find.byTooltip(tr);
|
||||
await tapButton(
|
||||
button,
|
||||
milliseconds: milliseconds,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Future<void> wait(int milliseconds) async {
|
||||
await pumpAndSettle(Duration(milliseconds: milliseconds));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'base.dart';
|
||||
|
||||
extension AppFlowyLaunch on WidgetTester {
|
||||
Future<void> tapGoButton() async {
|
||||
await tapButtonWithName(LocaleKeys.letsGoButtonText.tr());
|
||||
return;
|
||||
}
|
||||
|
||||
Future<void> tapCreateButton() async {
|
||||
await tapButtonWithName(LocaleKeys.settings_files_create.tr());
|
||||
return;
|
||||
}
|
||||
|
||||
Future<void> expectToSeeWelcomePage() async {
|
||||
expect(find.byType(HomeStack), findsOneWidget);
|
||||
expect(find.textContaining('Read me'), findsNWidgets(2));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/util/file_picker/file_picker_impl.dart';
|
||||
import 'package:app_flowy/util/file_picker/file_picker_service.dart';
|
||||
|
||||
import '../util.dart';
|
||||
|
||||
class MockFilePicker extends FilePicker {
|
||||
MockFilePicker({
|
||||
required this.mockPath,
|
||||
});
|
||||
|
||||
final String mockPath;
|
||||
|
||||
@override
|
||||
Future<String?> getDirectoryPath({String? title}) {
|
||||
return Future.value(mockPath);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> mockGetDirectoryPath(String? name) async {
|
||||
final dir = await TestFolder.testLocation(name);
|
||||
getIt.unregister<FilePickerService>();
|
||||
getIt.registerFactory<FilePickerService>(
|
||||
() => MockFilePicker(
|
||||
mockPath: dir.path,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:app_flowy/workspace/presentation/settings/settings_dialog.dart';
|
||||
import 'package:app_flowy/workspace/presentation/settings/widgets/settings_user_view.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'base.dart';
|
||||
|
||||
enum SettingsPage {
|
||||
appearance,
|
||||
language,
|
||||
files,
|
||||
user,
|
||||
}
|
||||
|
||||
extension on SettingsPage {
|
||||
String get name {
|
||||
switch (this) {
|
||||
case SettingsPage.appearance:
|
||||
return LocaleKeys.settings_menu_appearance.tr();
|
||||
case SettingsPage.language:
|
||||
return LocaleKeys.settings_menu_language.tr();
|
||||
case SettingsPage.files:
|
||||
return LocaleKeys.settings_menu_files.tr();
|
||||
case SettingsPage.user:
|
||||
return LocaleKeys.settings_menu_user.tr();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AppFlowySettings on WidgetTester {
|
||||
/// Open settings page
|
||||
Future<void> openSettings() async {
|
||||
final settingsButton = find.byTooltip(LocaleKeys.settings_menu_open.tr());
|
||||
expect(settingsButton, findsOneWidget);
|
||||
await tapButton(settingsButton);
|
||||
final settingsDialog = find.byType(SettingsDialog);
|
||||
expect(settingsDialog, findsOneWidget);
|
||||
return;
|
||||
}
|
||||
|
||||
/// Open the page taht insides the settings page
|
||||
Future<void> openSettingsPage(SettingsPage page) async {
|
||||
final button = find.text(page.name, findRichText: true);
|
||||
expect(button, findsOneWidget);
|
||||
await tapButton(button);
|
||||
return;
|
||||
}
|
||||
|
||||
/// Restore the AppFlowy data storage location
|
||||
Future<void> restoreLocation() async {
|
||||
final buton =
|
||||
find.byTooltip(LocaleKeys.settings_files_restoreLocation.tr());
|
||||
expect(buton, findsOneWidget);
|
||||
await tapButton(buton);
|
||||
return;
|
||||
}
|
||||
|
||||
Future<void> tapOpenFolderButton() async {
|
||||
final buton = find.text(LocaleKeys.settings_files_open.tr());
|
||||
expect(buton, findsOneWidget);
|
||||
await tapButton(buton);
|
||||
return;
|
||||
}
|
||||
|
||||
Future<void> tapCustomLocationButton() async {
|
||||
final buton =
|
||||
find.byTooltip(LocaleKeys.settings_files_customizeLocation.tr());
|
||||
expect(buton, findsOneWidget);
|
||||
await tapButton(buton);
|
||||
return;
|
||||
}
|
||||
|
||||
/// Enter user name
|
||||
Future<void> enterUserName(String name) async {
|
||||
final uni = find.byType(UserNameInput);
|
||||
expect(uni, findsOneWidget);
|
||||
await tap(uni);
|
||||
await enterText(uni, name);
|
||||
await wait(300); //
|
||||
await testTextInput.receiveAction(TextInputAction.done);
|
||||
await pumpAndSettle();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export 'base.dart';
|
||||
export 'launch.dart';
|
||||
export 'settings.dart';
|
||||
@@ -3,7 +3,7 @@ PODS:
|
||||
- Flutter
|
||||
- flowy_infra_ui (0.0.1):
|
||||
- Flutter
|
||||
- flowy_sdk (0.0.1):
|
||||
- appflowy_backend (0.0.1):
|
||||
- Flutter
|
||||
- Flutter (1.0.0)
|
||||
- flutter_inappwebview (0.0.1):
|
||||
@@ -28,7 +28,7 @@ PODS:
|
||||
DEPENDENCIES:
|
||||
- flowy_editor (from `.symlinks/plugins/flowy_editor/ios`)
|
||||
- flowy_infra_ui (from `.symlinks/plugins/flowy_infra_ui/ios`)
|
||||
- flowy_sdk (from `.symlinks/plugins/flowy_sdk/ios`)
|
||||
- appflowy_backend (from `.symlinks/plugins/appflowy_backend/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`)
|
||||
- flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
|
||||
@@ -46,8 +46,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/flowy_editor/ios"
|
||||
flowy_infra_ui:
|
||||
:path: ".symlinks/plugins/flowy_infra_ui/ios"
|
||||
flowy_sdk:
|
||||
:path: ".symlinks/plugins/flowy_sdk/ios"
|
||||
appflowy_backend:
|
||||
:path: ".symlinks/plugins/appflowy_backend/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
flutter_inappwebview:
|
||||
@@ -66,7 +66,7 @@ EXTERNAL SOURCES:
|
||||
SPEC CHECKSUMS:
|
||||
flowy_editor: bf8d58894ddb03453bd4d8521c57267ad638b837
|
||||
flowy_infra_ui: 146c88346fd55d2ee6a41ae35059a5bf095cfbb3
|
||||
flowy_sdk: c416222c639e678828776789bf0c1a1d0d59df3c
|
||||
appflowy_backend: c416222c639e678828776789bf0c1a1d0d59df3c
|
||||
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
|
||||
flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721
|
||||
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:flowy_sdk/protobuf/dart-notify/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-notification/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 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/notification.pb.dart';
|
||||
import 'package:appflowy_backend/rust_stream.dart';
|
||||
|
||||
import 'notification_helper.dart';
|
||||
|
||||
// Folder
|
||||
typedef FolderNotificationCallback = void Function(FolderNotification, Either<Uint8List, FlowyError>);
|
||||
typedef FolderNotificationCallback = void Function(
|
||||
FolderNotification,
|
||||
Either<Uint8List, FlowyError>,
|
||||
);
|
||||
|
||||
class FolderNotificationParser extends NotificationParser<FolderNotification, FlowyError> {
|
||||
FolderNotificationParser({String? id, required FolderNotificationCallback callback})
|
||||
class FolderNotificationParser
|
||||
extends NotificationParser<FolderNotification, FlowyError> {
|
||||
FolderNotificationParser(
|
||||
{String? id, required FolderNotificationCallback callback})
|
||||
: super(
|
||||
id: id,
|
||||
callback: callback,
|
||||
@@ -21,15 +26,24 @@ class FolderNotificationParser extends NotificationParser<FolderNotification, Fl
|
||||
);
|
||||
}
|
||||
|
||||
typedef FolderNotificationHandler = Function(FolderNotification ty, Either<Uint8List, FlowyError> result);
|
||||
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));
|
||||
FolderNotificationListener({
|
||||
required String objectId,
|
||||
required FolderNotificationHandler handler,
|
||||
}) : _parser = FolderNotificationParser(
|
||||
id: objectId,
|
||||
callback: handler,
|
||||
) {
|
||||
_subscription =
|
||||
RustStreamReceiver.listen((observable) => _parser?.parse(observable));
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
|
||||
@@ -1,39 +1,42 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:flowy_sdk/protobuf/dart-notify/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-notification/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 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/notification.pb.dart';
|
||||
import 'package:appflowy_backend/rust_stream.dart';
|
||||
|
||||
import 'notification_helper.dart';
|
||||
|
||||
// GridPB
|
||||
typedef GridNotificationCallback = void Function(
|
||||
GridDartNotification, Either<Uint8List, FlowyError>);
|
||||
// DatabasePB
|
||||
typedef DatabaseNotificationCallback = void Function(
|
||||
DatabaseNotification,
|
||||
Either<Uint8List, FlowyError>,
|
||||
);
|
||||
|
||||
class GridNotificationParser
|
||||
extends NotificationParser<GridDartNotification, FlowyError> {
|
||||
GridNotificationParser(
|
||||
{String? id, required GridNotificationCallback callback})
|
||||
class DatabaseNotificationParser
|
||||
extends NotificationParser<DatabaseNotification, FlowyError> {
|
||||
DatabaseNotificationParser(
|
||||
{String? id, required DatabaseNotificationCallback callback})
|
||||
: super(
|
||||
id: id,
|
||||
callback: callback,
|
||||
tyParser: (ty) => GridDartNotification.valueOf(ty),
|
||||
tyParser: (ty) => DatabaseNotification.valueOf(ty),
|
||||
errorParser: (bytes) => FlowyError.fromBuffer(bytes),
|
||||
);
|
||||
}
|
||||
|
||||
typedef GridNotificationHandler = Function(
|
||||
GridDartNotification ty, Either<Uint8List, FlowyError> result);
|
||||
typedef DatabaseNotificationHandler = Function(
|
||||
DatabaseNotification ty, Either<Uint8List, FlowyError> result);
|
||||
|
||||
class GridNotificationListener {
|
||||
class DatabaseNotificationListener {
|
||||
StreamSubscription<SubscribeObject>? _subscription;
|
||||
GridNotificationParser? _parser;
|
||||
DatabaseNotificationParser? _parser;
|
||||
|
||||
GridNotificationListener(
|
||||
{required String objectId, required GridNotificationHandler handler})
|
||||
: _parser = GridNotificationParser(id: objectId, callback: handler) {
|
||||
DatabaseNotificationListener({
|
||||
required String objectId,
|
||||
required DatabaseNotificationHandler handler,
|
||||
}) : _parser = DatabaseNotificationParser(id: objectId, callback: handler) {
|
||||
_subscription =
|
||||
RustStreamReceiver.listen((observable) => _parser?.parse(observable));
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-net/network_state.pb.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-net/network_state.pb.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class NetworkListener {
|
||||
@@ -35,21 +35,21 @@ class NetworkListener {
|
||||
final networkType = () {
|
||||
switch (result) {
|
||||
case ConnectivityResult.wifi:
|
||||
return NetworkType.Wifi;
|
||||
return NetworkTypePB.Wifi;
|
||||
case ConnectivityResult.ethernet:
|
||||
return NetworkType.Ethernet;
|
||||
return NetworkTypePB.Ethernet;
|
||||
case ConnectivityResult.mobile:
|
||||
return NetworkType.Cell;
|
||||
return NetworkTypePB.Cell;
|
||||
case ConnectivityResult.bluetooth:
|
||||
return NetworkType.Bluetooth;
|
||||
return NetworkTypePB.Bluetooth;
|
||||
case ConnectivityResult.vpn:
|
||||
return NetworkType.VPN;
|
||||
return NetworkTypePB.VPN;
|
||||
case ConnectivityResult.none:
|
||||
return NetworkType.UnknownNetworkType;
|
||||
return NetworkTypePB.Unknown;
|
||||
}
|
||||
}();
|
||||
Log.info("Network type: $networkType");
|
||||
final state = NetworkState.create()..ty = networkType;
|
||||
final state = NetworkStatePB.create()..ty = networkType;
|
||||
NetworkEventUpdateNetworkType(state).send().then((result) {
|
||||
result.fold(
|
||||
(l) {},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'dart:typed_data';
|
||||
import 'package:flowy_sdk/protobuf/dart-notify/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-notification/protobuf.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
class NotificationParser<T, E> {
|
||||
@@ -9,7 +9,11 @@ class NotificationParser<T, E> {
|
||||
T? Function(int) tyParser;
|
||||
E Function(Uint8List) errorParser;
|
||||
|
||||
NotificationParser({this.id, required this.callback, required this.errorParser, required this.tyParser});
|
||||
NotificationParser(
|
||||
{this.id,
|
||||
required this.callback,
|
||||
required this.errorParser,
|
||||
required this.tyParser});
|
||||
void parse(SubscribeObject subject) {
|
||||
if (id != null) {
|
||||
if (subject.id != id) {
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
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:appflowy_backend/protobuf/flowy-notification/protobuf.dart';
|
||||
import 'package:appflowy_backend/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 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/rust_stream.dart';
|
||||
|
||||
import 'notification_helper.dart';
|
||||
|
||||
// User
|
||||
typedef UserNotificationCallback = void Function(UserNotification, Either<Uint8List, FlowyError>);
|
||||
typedef UserNotificationCallback = void Function(
|
||||
UserNotification,
|
||||
Either<Uint8List, FlowyError>,
|
||||
);
|
||||
|
||||
class UserNotificationParser extends NotificationParser<UserNotification, FlowyError> {
|
||||
UserNotificationParser({required String id, required UserNotificationCallback callback})
|
||||
class UserNotificationParser
|
||||
extends NotificationParser<UserNotification, FlowyError> {
|
||||
UserNotificationParser(
|
||||
{required String id, required UserNotificationCallback callback})
|
||||
: super(
|
||||
id: id,
|
||||
callback: callback,
|
||||
@@ -21,15 +26,19 @@ class UserNotificationParser extends NotificationParser<UserNotification, FlowyE
|
||||
);
|
||||
}
|
||||
|
||||
typedef UserNotificationHandler = Function(UserNotification ty, Either<Uint8List, FlowyError> result);
|
||||
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));
|
||||
UserNotificationListener({
|
||||
required String objectId,
|
||||
required UserNotificationHandler handler,
|
||||
}) : _parser = UserNotificationParser(id: objectId, callback: handler) {
|
||||
_subscription =
|
||||
RustStreamReceiver.listen((observable) => _parser?.parse(observable));
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/user/presentation/splash_screen.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||
|
||||
import 'startup/launch_configuration.dart';
|
||||
import 'startup/startup.dart';
|
||||
import 'user/presentation/splash_screen.dart';
|
||||
|
||||
class FlowyApp implements EntryPoint {
|
||||
@override
|
||||
Widget create() {
|
||||
return const SplashScreen();
|
||||
Widget create(LaunchConfiguration config) {
|
||||
return SplashScreen(
|
||||
autoRegister: config.autoRegistrationSupported,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void main() async {
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await EasyLocalization.ensureInitialized();
|
||||
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:app_flowy/plugins/grid/application/block/block_cache.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/row/row_service.dart';
|
||||
import 'package:appflowy_board/appflowy_board.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
@@ -29,10 +28,10 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
|
||||
GridFieldController get fieldController =>
|
||||
_gridDataController.fieldController;
|
||||
String get gridId => _gridDataController.gridId;
|
||||
String get databaseId => _gridDataController.viewId;
|
||||
|
||||
BoardBloc({required ViewPB view})
|
||||
: _rowService = MoveRowFFIService(gridId: view.id),
|
||||
: _rowService = MoveRowFFIService(viewId: view.id),
|
||||
_gridDataController = BoardDataController(view: view),
|
||||
super(BoardState.initial(view.id)) {
|
||||
boardController = AppFlowyBoardController(
|
||||
@@ -117,7 +116,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
emit(state.copyWith(editingRow: none()));
|
||||
});
|
||||
},
|
||||
didReceiveGridUpdate: (GridPB grid) {
|
||||
didReceiveGridUpdate: (DatabasePB grid) {
|
||||
emit(state.copyWith(grid: Some(grid)));
|
||||
},
|
||||
didReceiveError: (FlowyError error) {
|
||||
@@ -187,50 +186,26 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
return super.close();
|
||||
}
|
||||
|
||||
void initializeGroups(List<GroupPB> groupsData) {
|
||||
void initializeGroups(List<GroupPB> groups) {
|
||||
for (var controller in groupControllers.values) {
|
||||
controller.dispose();
|
||||
}
|
||||
groupControllers.clear();
|
||||
boardController.clear();
|
||||
|
||||
//
|
||||
List<AppFlowyGroupData> groups = groupsData
|
||||
boardController.addGroups(groups
|
||||
.where((group) => fieldController.getField(group.fieldId) != null)
|
||||
.map((group) {
|
||||
return AppFlowyGroupData(
|
||||
id: group.groupId,
|
||||
name: group.desc,
|
||||
items: _buildGroupItems(group),
|
||||
customData: GroupData(
|
||||
group: group,
|
||||
fieldInfo: fieldController.getField(group.fieldId)!,
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
boardController.addGroups(groups);
|
||||
.map((group) => initializeGroupData(group))
|
||||
.toList());
|
||||
|
||||
for (final group in groupsData) {
|
||||
final delegate = GroupControllerDelegateImpl(
|
||||
controller: boardController,
|
||||
fieldController: fieldController,
|
||||
onNewColumnItem: (groupId, row, index) {
|
||||
add(BoardEvent.didCreateRow(group, row, index));
|
||||
},
|
||||
);
|
||||
final controller = GroupController(
|
||||
gridId: state.gridId,
|
||||
group: group,
|
||||
delegate: delegate,
|
||||
);
|
||||
controller.startListening();
|
||||
for (final group in groups) {
|
||||
final controller = initializeGroupController(group);
|
||||
groupControllers[controller.group.groupId] = (controller);
|
||||
}
|
||||
}
|
||||
|
||||
GridRowCache? getRowCache(String blockId) {
|
||||
final GridBlockCache? blockCache = _gridDataController.blocks[blockId];
|
||||
return blockCache?.rowCache;
|
||||
return _gridDataController.rowCache;
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
@@ -247,11 +222,15 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
},
|
||||
onDeletedGroup: (groupIds) {
|
||||
if (isClosed) return;
|
||||
//
|
||||
boardController.removeGroups(groupIds);
|
||||
},
|
||||
onInsertedGroup: (insertedGroups) {
|
||||
onInsertedGroup: (insertedGroup) {
|
||||
if (isClosed) return;
|
||||
//
|
||||
final group = insertedGroup.group;
|
||||
final newGroup = initializeGroupData(group);
|
||||
final controller = initializeGroupController(group);
|
||||
groupControllers[controller.group.groupId] = (controller);
|
||||
boardController.addGroup(newGroup);
|
||||
},
|
||||
onUpdatedGroup: (updatedGroups) {
|
||||
if (isClosed) return;
|
||||
@@ -296,6 +275,35 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
GroupController initializeGroupController(GroupPB group) {
|
||||
final delegate = GroupControllerDelegateImpl(
|
||||
controller: boardController,
|
||||
fieldController: fieldController,
|
||||
onNewColumnItem: (groupId, row, index) {
|
||||
add(BoardEvent.didCreateRow(group, row, index));
|
||||
},
|
||||
);
|
||||
final controller = GroupController(
|
||||
databaseId: state.databaseId,
|
||||
group: group,
|
||||
delegate: delegate,
|
||||
);
|
||||
controller.startListening();
|
||||
return controller;
|
||||
}
|
||||
|
||||
AppFlowyGroupData initializeGroupData(GroupPB group) {
|
||||
return AppFlowyGroupData(
|
||||
id: group.groupId,
|
||||
name: group.desc,
|
||||
items: _buildGroupItems(group),
|
||||
customData: GroupData(
|
||||
group: group,
|
||||
fieldInfo: fieldController.getField(group.fieldId)!,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
@@ -315,7 +323,7 @@ class BoardEvent with _$BoardEvent {
|
||||
const factory BoardEvent.endEditingRow(String rowId) = _EndEditRow;
|
||||
const factory BoardEvent.didReceiveError(FlowyError error) = _DidReceiveError;
|
||||
const factory BoardEvent.didReceiveGridUpdate(
|
||||
GridPB grid,
|
||||
DatabasePB grid,
|
||||
) = _DidReceiveGridUpdate;
|
||||
const factory BoardEvent.didReceiveGroups(List<GroupPB> groups) =
|
||||
_DidReceiveGroups;
|
||||
@@ -324,17 +332,17 @@ class BoardEvent with _$BoardEvent {
|
||||
@freezed
|
||||
class BoardState with _$BoardState {
|
||||
const factory BoardState({
|
||||
required String gridId,
|
||||
required Option<GridPB> grid,
|
||||
required String databaseId,
|
||||
required Option<DatabasePB> grid,
|
||||
required List<String> groupIds,
|
||||
required Option<BoardEditingRow> editingRow,
|
||||
required GridLoadingState loadingState,
|
||||
required Option<FlowyError> noneOrError,
|
||||
}) = _BoardState;
|
||||
|
||||
factory BoardState.initial(String gridId) => BoardState(
|
||||
factory BoardState.initial(String databaseId) => BoardState(
|
||||
grid: none(),
|
||||
gridId: gridId,
|
||||
databaseId: databaseId,
|
||||
groupIds: [],
|
||||
editingRow: none(),
|
||||
noneOrError: none(),
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:app_flowy/plugins/grid/application/block/block_cache.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/view/grid_view_cache.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/grid_service.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'dart:async';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
|
||||
|
||||
import 'board_listener.dart';
|
||||
|
||||
typedef OnFieldsChanged = void Function(UnmodifiableListView<FieldInfo>);
|
||||
typedef OnGridChanged = void Function(GridPB);
|
||||
typedef OnGridChanged = void Function(DatabasePB);
|
||||
typedef DidLoadGroups = void Function(List<GroupPB>);
|
||||
typedef OnUpdatedGroup = void Function(List<GroupPB>);
|
||||
typedef OnDeletedGroup = void Function(List<String>);
|
||||
typedef OnInsertedGroup = void Function(List<InsertedGroupPB>);
|
||||
typedef OnInsertedGroup = void Function(InsertedGroupPB);
|
||||
typedef OnResetGroups = void Function(List<GroupPB>);
|
||||
|
||||
typedef OnRowsChanged = void Function(
|
||||
@@ -27,15 +27,11 @@ typedef OnRowsChanged = void Function(
|
||||
typedef OnError = void Function(FlowyError);
|
||||
|
||||
class BoardDataController {
|
||||
final String gridId;
|
||||
final GridFFIService _gridFFIService;
|
||||
final String viewId;
|
||||
final DatabaseFFIService _databaseFFIService;
|
||||
final GridFieldController fieldController;
|
||||
final BoardListener _listener;
|
||||
|
||||
// key: the block id
|
||||
final LinkedHashMap<String, GridBlockCache> _blocks;
|
||||
UnmodifiableMapView<String, GridBlockCache> get blocks =>
|
||||
UnmodifiableMapView(_blocks);
|
||||
late DatabaseViewCache _viewCache;
|
||||
|
||||
OnFieldsChanged? _onFieldsChanged;
|
||||
OnGridChanged? _onGridChanged;
|
||||
@@ -43,21 +39,23 @@ class BoardDataController {
|
||||
OnRowsChanged? _onRowsChanged;
|
||||
OnError? _onError;
|
||||
|
||||
List<RowInfo> get rowInfos {
|
||||
final List<RowInfo> rows = [];
|
||||
for (var block in _blocks.values) {
|
||||
rows.addAll(block.rows);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
List<RowInfo> get rowInfos => _viewCache.rowInfos;
|
||||
GridRowCache get rowCache => _viewCache.rowCache;
|
||||
|
||||
BoardDataController({required ViewPB view})
|
||||
: gridId = view.id,
|
||||
: viewId = view.id,
|
||||
_listener = BoardListener(view.id),
|
||||
// ignore: prefer_collection_literals
|
||||
_blocks = LinkedHashMap(),
|
||||
_gridFFIService = GridFFIService(gridId: view.id),
|
||||
fieldController = GridFieldController(gridId: view.id);
|
||||
_databaseFFIService = DatabaseFFIService(databaseId: view.id),
|
||||
fieldController = GridFieldController(databaseId: view.id) {
|
||||
//
|
||||
_viewCache = DatabaseViewCache(
|
||||
databaseId: view.id,
|
||||
fieldController: fieldController,
|
||||
);
|
||||
_viewCache.addListener(onRowsChanged: (reason) {
|
||||
_onRowsChanged?.call(rowInfos, reason);
|
||||
});
|
||||
}
|
||||
|
||||
void addListener({
|
||||
required OnGridChanged onGridChanged,
|
||||
@@ -92,8 +90,8 @@ class BoardDataController {
|
||||
onDeletedGroup.call(changeset.deletedGroups);
|
||||
}
|
||||
|
||||
if (changeset.insertedGroups.isNotEmpty) {
|
||||
onInsertedGroup.call(changeset.insertedGroups);
|
||||
for (final insertedGroup in changeset.insertedGroups) {
|
||||
onInsertedGroup.call(insertedGroup);
|
||||
}
|
||||
},
|
||||
(e) => _onError?.call(e),
|
||||
@@ -109,56 +107,38 @@ class BoardDataController {
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> openGrid() async {
|
||||
final result = await _gridFFIService.openGrid();
|
||||
return Future(
|
||||
() => result.fold(
|
||||
(grid) async {
|
||||
_onGridChanged?.call(grid);
|
||||
final result = await fieldController.loadFields(
|
||||
fieldIds: grid.fields,
|
||||
);
|
||||
final result = await _databaseFFIService.openGrid();
|
||||
return result.fold(
|
||||
(grid) async {
|
||||
_onGridChanged?.call(grid);
|
||||
return fieldController.loadFields(fieldIds: grid.fields).then((result) {
|
||||
return result.fold(
|
||||
(l) {
|
||||
_loadGroups(grid.blocks);
|
||||
(l) => Future(() async {
|
||||
await _loadGroups();
|
||||
_viewCache.rowCache.initializeRows(grid.rows);
|
||||
return left(l);
|
||||
},
|
||||
}),
|
||||
(err) => right(err),
|
||||
);
|
||||
},
|
||||
(err) => right(err),
|
||||
),
|
||||
});
|
||||
},
|
||||
(err) => right(err),
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<RowPB, FlowyError>> createBoardCard(String groupId,
|
||||
{String? startRowId}) {
|
||||
return _gridFFIService.createBoardCard(groupId, startRowId);
|
||||
return _databaseFFIService.createBoardCard(groupId, startRowId);
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
await _gridFFIService.closeGrid();
|
||||
await _viewCache.dispose();
|
||||
await _databaseFFIService.closeGrid();
|
||||
await fieldController.dispose();
|
||||
|
||||
for (final blockCache in _blocks.values) {
|
||||
blockCache.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadGroups(List<BlockPB> blocks) async {
|
||||
for (final block in blocks) {
|
||||
final cache = GridBlockCache(
|
||||
gridId: gridId,
|
||||
block: block,
|
||||
fieldController: fieldController,
|
||||
);
|
||||
|
||||
cache.addListener(onRowsChanged: (reason) {
|
||||
_onRowsChanged?.call(rowInfos, reason);
|
||||
});
|
||||
_blocks[block.id] = cache;
|
||||
}
|
||||
|
||||
final result = await _gridFFIService.loadGroups();
|
||||
Future<void> _loadGroups() async {
|
||||
final result = await _databaseFFIService.loadGroups();
|
||||
return Future(
|
||||
() => result.fold(
|
||||
(groups) {
|
||||
|
||||
@@ -2,13 +2,13 @@ import 'dart:typed_data';
|
||||
|
||||
import 'package:app_flowy/core/grid_notification.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/notification.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/group_changeset.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/group.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/group_changeset.pb.dart';
|
||||
|
||||
typedef GroupUpdateValue = Either<GroupViewChangesetPB, FlowyError>;
|
||||
typedef GroupUpdateValue = Either<GroupChangesetPB, FlowyError>;
|
||||
typedef GroupByNewFieldValue = Either<List<GroupPB>, FlowyError>;
|
||||
|
||||
class BoardListener {
|
||||
@@ -16,7 +16,7 @@ class BoardListener {
|
||||
PublishNotifier<GroupUpdateValue>? _groupUpdateNotifier = PublishNotifier();
|
||||
PublishNotifier<GroupByNewFieldValue>? _groupByNewFieldNotifier =
|
||||
PublishNotifier();
|
||||
GridNotificationListener? _listener;
|
||||
DatabaseNotificationListener? _listener;
|
||||
BoardListener(this.viewId);
|
||||
|
||||
void start({
|
||||
@@ -25,28 +25,28 @@ class BoardListener {
|
||||
}) {
|
||||
_groupUpdateNotifier?.addPublishListener(onBoardChanged);
|
||||
_groupByNewFieldNotifier?.addPublishListener(onGroupByNewField);
|
||||
_listener = GridNotificationListener(
|
||||
_listener = DatabaseNotificationListener(
|
||||
objectId: viewId,
|
||||
handler: _handler,
|
||||
);
|
||||
}
|
||||
|
||||
void _handler(
|
||||
GridDartNotification ty,
|
||||
DatabaseNotification ty,
|
||||
Either<Uint8List, FlowyError> result,
|
||||
) {
|
||||
switch (ty) {
|
||||
case GridDartNotification.DidUpdateGroupView:
|
||||
case DatabaseNotification.DidUpdateGroups:
|
||||
result.fold(
|
||||
(payload) => _groupUpdateNotifier?.value =
|
||||
left(GroupViewChangesetPB.fromBuffer(payload)),
|
||||
left(GroupChangesetPB.fromBuffer(payload)),
|
||||
(error) => _groupUpdateNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
case GridDartNotification.DidGroupByNewField:
|
||||
case DatabaseNotification.DidGroupByField:
|
||||
result.fold(
|
||||
(payload) => _groupByNewFieldNotifier?.value =
|
||||
left(GroupViewChangesetPB.fromBuffer(payload).newGroups),
|
||||
left(GroupChangesetPB.fromBuffer(payload).initialGroups),
|
||||
(error) => _groupByNewFieldNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
|
||||
@@ -64,7 +64,7 @@ class BoardCheckboxCellState with _$BoardCheckboxCellState {
|
||||
required bool isSelected,
|
||||
}) = _CheckboxCellState;
|
||||
|
||||
factory BoardCheckboxCellState.initial(GridCellController context) {
|
||||
factory BoardCheckboxCellState.initial(GridTextCellController context) {
|
||||
return BoardCheckboxCellState(
|
||||
isSelected: _isSelected(context.getCellData()));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/date_type_option_entities.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
|
||||
@@ -60,7 +60,7 @@ class BoardNumberCellState with _$BoardNumberCellState {
|
||||
required String content,
|
||||
}) = _BoardNumberCellState;
|
||||
|
||||
factory BoardNumberCellState.initial(GridCellController context) =>
|
||||
factory BoardNumberCellState.initial(GridTextCellController context) =>
|
||||
BoardNumberCellState(
|
||||
content: context.getCellData() ?? "",
|
||||
);
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
@@ -7,7 +6,7 @@ import 'dart:async';
|
||||
part 'board_text_cell_bloc.freezed.dart';
|
||||
|
||||
class BoardTextCellBloc extends Bloc<BoardTextCellEvent, BoardTextCellState> {
|
||||
final GridCellController cellController;
|
||||
final GridTextCellController cellController;
|
||||
void Function()? _onCellChangedFn;
|
||||
BoardTextCellBloc({
|
||||
required this.cellController,
|
||||
@@ -72,7 +71,7 @@ class BoardTextCellState with _$BoardTextCellState {
|
||||
required bool enableEdit,
|
||||
}) = _BoardTextCellState;
|
||||
|
||||
factory BoardTextCellState.initial(GridCellController context) =>
|
||||
factory BoardTextCellState.initial(GridTextCellController context) =>
|
||||
BoardTextCellState(
|
||||
content: context.getCellData() ?? "",
|
||||
enableEdit: false,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/url_type_option_entities.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_servic
|
||||
import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/row/row_service.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
@@ -18,12 +18,11 @@ class BoardCardBloc extends Bloc<BoardCardEvent, BoardCardState> {
|
||||
|
||||
BoardCardBloc({
|
||||
required this.groupFieldId,
|
||||
required String gridId,
|
||||
required String viewId,
|
||||
required CardDataController dataController,
|
||||
required bool isEditing,
|
||||
}) : _rowService = RowFFIService(
|
||||
gridId: gridId,
|
||||
blockId: dataController.rowPB.blockId,
|
||||
databaseId: viewId,
|
||||
),
|
||||
_dataController = dataController,
|
||||
super(
|
||||
@@ -61,7 +60,7 @@ class BoardCardBloc extends Bloc<BoardCardEvent, BoardCardState> {
|
||||
|
||||
RowInfo rowInfo() {
|
||||
return RowInfo(
|
||||
gridId: _rowService.gridId,
|
||||
databaseId: _rowService.databaseId,
|
||||
fields: UnmodifiableListView(
|
||||
state.cells.map((cell) => cell.identifier.fieldInfo).toList(),
|
||||
),
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_servic
|
||||
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_field_notifier.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
typedef OnCardChanged = void Function(GridCellMap, RowsChangedReason);
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
|
||||
|
||||
class BoardGroupService {
|
||||
final String gridId;
|
||||
final String viewId;
|
||||
FieldPB? groupField;
|
||||
|
||||
BoardGroupService(this.gridId);
|
||||
BoardGroupService(this.viewId);
|
||||
|
||||
void setGroupField(FieldPB field) {
|
||||
groupField = field;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class CanBeGroupField {
|
||||
String get groupContent;
|
||||
}
|
||||
|
||||
// class SingleSelectGroup extends CanBeGroupField {
|
||||
// final SingleSelectTypeOptionContext typeOptionContext;
|
||||
// }
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
|
||||
import 'group_listener.dart';
|
||||
|
||||
typedef OnGroupError = void Function(FlowyError);
|
||||
@@ -18,7 +18,7 @@ class GroupController {
|
||||
final GroupControllerDelegate delegate;
|
||||
|
||||
GroupController({
|
||||
required String gridId,
|
||||
required String databaseId,
|
||||
required this.group,
|
||||
required this.delegate,
|
||||
}) : _listener = GroupListener(group);
|
||||
|
||||
@@ -2,36 +2,36 @@ import 'dart:typed_data';
|
||||
|
||||
import 'package:app_flowy/core/grid_notification.dart';
|
||||
import 'package:flowy_infra/notifier.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/group.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/notification.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/group.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/group_changeset.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/group_changeset.pb.dart';
|
||||
|
||||
typedef UpdateGroupNotifiedValue = Either<GroupRowsNotificationPB, FlowyError>;
|
||||
|
||||
class GroupListener {
|
||||
final GroupPB group;
|
||||
PublishNotifier<UpdateGroupNotifiedValue>? _groupNotifier = PublishNotifier();
|
||||
GridNotificationListener? _listener;
|
||||
DatabaseNotificationListener? _listener;
|
||||
GroupListener(this.group);
|
||||
|
||||
void start({
|
||||
required void Function(UpdateGroupNotifiedValue) onGroupChanged,
|
||||
}) {
|
||||
_groupNotifier?.addPublishListener(onGroupChanged);
|
||||
_listener = GridNotificationListener(
|
||||
_listener = DatabaseNotificationListener(
|
||||
objectId: group.groupId,
|
||||
handler: _handler,
|
||||
);
|
||||
}
|
||||
|
||||
void _handler(
|
||||
GridDartNotification ty,
|
||||
DatabaseNotification ty,
|
||||
Either<Uint8List, FlowyError> result,
|
||||
) {
|
||||
switch (ty) {
|
||||
case GridDartNotification.DidUpdateGroup:
|
||||
case DatabaseNotification.DidUpdateGroupRow:
|
||||
result.fold(
|
||||
(payload) => _groupNotifier?.value =
|
||||
left(GroupRowsNotificationPB.fromBuffer(payload)),
|
||||
|
||||
@@ -6,8 +6,8 @@ import 'package:dartz/dartz.dart';
|
||||
part 'board_setting_bloc.freezed.dart';
|
||||
|
||||
class BoardSettingBloc extends Bloc<BoardSettingEvent, BoardSettingState> {
|
||||
final String gridId;
|
||||
BoardSettingBloc({required this.gridId})
|
||||
final String databaseId;
|
||||
BoardSettingBloc({required this.databaseId})
|
||||
: super(BoardSettingState.initial()) {
|
||||
on<BoardSettingEvent>(
|
||||
(event, emit) async {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:app_flowy/plugins/util.dart';
|
||||
import 'package:app_flowy/startup/plugin/plugin.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/left_bar_item.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:app_flowy/startup/plugin/plugin.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'presentation/board_page.dart';
|
||||
@@ -18,7 +20,7 @@ class BoardPluginBuilder implements PluginBuilder {
|
||||
}
|
||||
|
||||
@override
|
||||
String get menuName => "Board";
|
||||
String get menuName => LocaleKeys.board_menuName.tr();
|
||||
|
||||
@override
|
||||
String get menuIcon => "editor/board";
|
||||
|
||||
@@ -4,35 +4,40 @@ import 'dart:collection';
|
||||
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:app_flowy/plugins/board/application/card/card_data_controller.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/row/row_data_controller.dart';
|
||||
import 'package:app_flowy/plugins/grid/presentation/widgets/cell/cell_builder.dart';
|
||||
import 'package:app_flowy/plugins/grid/presentation/widgets/row/row_detail.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
|
||||
import 'package:appflowy_board/appflowy_board.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder/view.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:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../grid/application/row/row_cache.dart';
|
||||
|
||||
import '../application/board_bloc.dart';
|
||||
import 'card/card.dart';
|
||||
import 'card/card_cell_builder.dart';
|
||||
import 'toolbar/board_toolbar.dart';
|
||||
|
||||
class BoardPage extends StatelessWidget {
|
||||
final ViewPB view;
|
||||
BoardPage({
|
||||
required this.view,
|
||||
Key? key,
|
||||
this.onEditStateChanged,
|
||||
}) : super(key: ValueKey(view.id));
|
||||
|
||||
final ViewPB view;
|
||||
|
||||
/// Called when edit state changed
|
||||
final VoidCallback? onEditStateChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
@@ -46,7 +51,9 @@ class BoardPage extends StatelessWidget {
|
||||
const Center(child: CircularProgressIndicator.adaptive()),
|
||||
finish: (result) {
|
||||
return result.successOrFail.fold(
|
||||
(_) => const BoardContent(),
|
||||
(_) => BoardContent(
|
||||
onEditStateChanged: onEditStateChanged,
|
||||
),
|
||||
(err) => FlowyErrorPage(err.toString()),
|
||||
);
|
||||
},
|
||||
@@ -58,7 +65,12 @@ class BoardPage extends StatelessWidget {
|
||||
}
|
||||
|
||||
class BoardContent extends StatefulWidget {
|
||||
const BoardContent({Key? key}) : super(key: key);
|
||||
const BoardContent({
|
||||
Key? key,
|
||||
this.onEditStateChanged,
|
||||
}) : super(key: key);
|
||||
|
||||
final VoidCallback? onEditStateChanged;
|
||||
|
||||
@override
|
||||
State<BoardContent> createState() => _BoardContentState();
|
||||
@@ -80,7 +92,10 @@ class _BoardContentState extends State<BoardContent> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocListener<BoardBloc, BoardState>(
|
||||
listener: (context, state) => _handleEditStateChanged(state, context),
|
||||
listener: (context, state) {
|
||||
_handleEditStateChanged(state, context);
|
||||
widget.onEditStateChanged?.call();
|
||||
},
|
||||
child: BlocBuilder<BoardBloc, BoardState>(
|
||||
buildWhen: (previous, current) => previous.groupIds != current.groupIds,
|
||||
builder: (context, state) {
|
||||
@@ -212,7 +227,7 @@ class _BoardContentState extends State<BoardContent> {
|
||||
if (rowCache == null) return SizedBox(key: ObjectKey(groupItem));
|
||||
|
||||
final fieldController = context.read<BoardBloc>().fieldController;
|
||||
final gridId = context.read<BoardBloc>().gridId;
|
||||
final databaseId = context.read<BoardBloc>().databaseId;
|
||||
final cardController = CardDataController(
|
||||
fieldController: fieldController,
|
||||
rowCache: rowCache,
|
||||
@@ -234,14 +249,14 @@ class _BoardContentState extends State<BoardContent> {
|
||||
margin: config.cardPadding,
|
||||
decoration: _makeBoxDecoration(context),
|
||||
child: BoardCard(
|
||||
gridId: gridId,
|
||||
viewId: databaseId,
|
||||
groupId: groupData.group.groupId,
|
||||
fieldId: groupItem.fieldInfo.id,
|
||||
isEditing: isEditing,
|
||||
cellBuilder: cellBuilder,
|
||||
dataController: cardController,
|
||||
openCard: (context) => _openCard(
|
||||
gridId,
|
||||
databaseId,
|
||||
fieldController,
|
||||
rowPB,
|
||||
rowCache,
|
||||
@@ -277,19 +292,19 @@ class _BoardContentState extends State<BoardContent> {
|
||||
}
|
||||
|
||||
void _openCard(
|
||||
String gridId,
|
||||
String databaseId,
|
||||
GridFieldController fieldController,
|
||||
RowPB rowPB,
|
||||
GridRowCache rowCache,
|
||||
BuildContext context,
|
||||
) {
|
||||
final rowInfo = RowInfo(
|
||||
gridId: gridId,
|
||||
databaseId: databaseId,
|
||||
fields: UnmodifiableListView(fieldController.fieldInfos),
|
||||
rowPB: rowPB,
|
||||
);
|
||||
|
||||
final dataController = GridRowDataController(
|
||||
final dataController = RowDataController(
|
||||
rowInfo: rowInfo,
|
||||
fieldController: fieldController,
|
||||
rowCache: rowCache,
|
||||
@@ -316,7 +331,7 @@ class _ToolbarBlocAdaptor extends StatelessWidget {
|
||||
builder: (context, state) {
|
||||
final bloc = context.read<BoardBloc>();
|
||||
final toolbarContext = BoardToolbarContext(
|
||||
viewId: bloc.gridId,
|
||||
viewId: bloc.databaseId,
|
||||
fieldController: bloc.fieldController,
|
||||
);
|
||||
|
||||
@@ -358,6 +373,8 @@ Widget? _buildHeaderIcon(GroupData customData) {
|
||||
break;
|
||||
case FieldType.URL:
|
||||
break;
|
||||
case FieldType.Checklist:
|
||||
break;
|
||||
}
|
||||
|
||||
if (widget != null) {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/cell/checklist_cell_bloc.dart';
|
||||
import 'package:app_flowy/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_progress_bar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class BoardChecklistCell extends StatefulWidget {
|
||||
final GridCellControllerBuilder cellControllerBuilder;
|
||||
const BoardChecklistCell({required this.cellControllerBuilder, Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<BoardChecklistCell> createState() => _BoardChecklistCellState();
|
||||
}
|
||||
|
||||
class _BoardChecklistCellState extends State<BoardChecklistCell> {
|
||||
late ChecklistCellBloc _cellBloc;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final cellController =
|
||||
widget.cellControllerBuilder.build() as GridChecklistCellController;
|
||||
_cellBloc = ChecklistCellBloc(cellController: cellController);
|
||||
_cellBloc.add(const ChecklistCellEvent.initial());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider.value(
|
||||
value: _cellBloc,
|
||||
child: BlocBuilder<ChecklistCellBloc, ChecklistCellState>(
|
||||
builder: (context, state) =>
|
||||
ChecklistProgressBar(percent: state.percent),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ class _BoardTextCellState extends State<BoardTextCell> {
|
||||
@override
|
||||
void initState() {
|
||||
final cellController =
|
||||
widget.cellControllerBuilder.build() as GridCellController;
|
||||
widget.cellControllerBuilder.build() as GridTextCellController;
|
||||
_cellBloc = BoardTextCellBloc(cellController: cellController)
|
||||
..add(const BoardTextCellEvent.initial());
|
||||
_controller = TextEditingController(text: _cellBloc.state.content);
|
||||
|
||||
@@ -13,7 +13,7 @@ import 'container/accessory.dart';
|
||||
import 'container/card_container.dart';
|
||||
|
||||
class BoardCard extends StatefulWidget {
|
||||
final String gridId;
|
||||
final String viewId;
|
||||
final String groupId;
|
||||
final String fieldId;
|
||||
final bool isEditing;
|
||||
@@ -24,7 +24,7 @@ class BoardCard extends StatefulWidget {
|
||||
final VoidCallback onEndEditing;
|
||||
|
||||
const BoardCard({
|
||||
required this.gridId,
|
||||
required this.viewId,
|
||||
required this.groupId,
|
||||
required this.fieldId,
|
||||
required this.isEditing,
|
||||
@@ -50,7 +50,7 @@ class _BoardCardState extends State<BoardCard> {
|
||||
void initState() {
|
||||
rowNotifier = EditableRowNotifier(isEditing: widget.isEditing);
|
||||
_cardBloc = BoardCardBloc(
|
||||
gridId: widget.gridId,
|
||||
viewId: widget.viewId,
|
||||
groupFieldId: widget.fieldId,
|
||||
dataController: widget.dataController,
|
||||
isEditing: widget.isEditing,
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'board_cell.dart';
|
||||
import 'board_checkbox_cell.dart';
|
||||
import 'board_checklist_cell.dart';
|
||||
import 'board_date_cell.dart';
|
||||
import 'board_number_cell.dart';
|
||||
import 'board_select_option_cell.dart';
|
||||
@@ -58,6 +59,11 @@ class BoardCellBuilder {
|
||||
editableNotifier: cellNotifier,
|
||||
key: key,
|
||||
);
|
||||
case FieldType.Checklist:
|
||||
return BoardChecklistCell(
|
||||
cellControllerBuilder: cellControllerBuilder,
|
||||
key: key,
|
||||
);
|
||||
case FieldType.Number:
|
||||
return BoardNumberCell(
|
||||
groupId: groupId,
|
||||
|
||||
@@ -13,6 +13,7 @@ import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
import 'board_toolbar.dart';
|
||||
|
||||
@@ -43,7 +44,7 @@ class BoardSettingList extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => BoardSettingBloc(gridId: settingContext.viewId),
|
||||
create: (context) => BoardSettingBloc(databaseId: settingContext.viewId),
|
||||
child: BlocListener<BoardSettingBloc, BoardSettingState>(
|
||||
listenWhen: (previous, current) =>
|
||||
previous.selectedAction != current.selectedAction,
|
||||
@@ -170,7 +171,7 @@ class _BoardSettingListPopoverState extends State<BoardSettingListPopover> {
|
||||
);
|
||||
case BoardSettingAction.properties:
|
||||
return GridPropertyList(
|
||||
gridId: widget.settingContext.viewId,
|
||||
databaseId: widget.settingContext.viewId,
|
||||
fieldController: widget.settingContext.fieldController,
|
||||
);
|
||||
}
|
||||
@@ -181,6 +182,6 @@ class _BoardSettingListPopoverState extends State<BoardSettingListPopover> {
|
||||
onAction: (action, settingContext) {
|
||||
setState(() => _action = action);
|
||||
},
|
||||
);
|
||||
).padding(all: 6.0);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
|
||||
import 'package:app_flowy/plugins/grid/presentation/layout/sizes.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/color_extension.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -67,12 +68,12 @@ class _SettingButtonState extends State<_SettingButton> {
|
||||
direction: PopoverDirection.leftWithTopAligned,
|
||||
triggerActions: PopoverTriggerFlags.none,
|
||||
constraints: BoxConstraints.loose(const Size(260, 400)),
|
||||
margin: EdgeInsets.zero,
|
||||
child: FlowyTextButton(
|
||||
LocaleKeys.settings_title.tr(),
|
||||
fontSize: 14,
|
||||
fillColor: Colors.transparent,
|
||||
hoverColor: AFThemeExtension.of(context).lightGreyHover,
|
||||
padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 6),
|
||||
padding: GridSize.typeOptionContentInsets,
|
||||
onPressed: () {
|
||||
popoverController.show();
|
||||
},
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:app_flowy/startup/plugin/plugin.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/left_bar_item.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../util.dart';
|
||||
import 'presentation/calendar_page.dart';
|
||||
|
||||
class CalendarPluginBuilder extends PluginBuilder {
|
||||
@override
|
||||
Plugin build(dynamic data) {
|
||||
if (data is ViewPB) {
|
||||
return CalendarPlugin(pluginType: pluginType, view: data);
|
||||
} else {
|
||||
throw FlowyPluginException.invalidData;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String get menuName => LocaleKeys.calendar_menuName.tr();
|
||||
|
||||
@override
|
||||
String get menuIcon => "editor/date";
|
||||
|
||||
@override
|
||||
PluginType get pluginType => PluginType.calendar;
|
||||
|
||||
@override
|
||||
ViewDataFormatPB get dataFormatType => ViewDataFormatPB.DatabaseFormat;
|
||||
|
||||
@override
|
||||
ViewLayoutTypePB? get layoutType => ViewLayoutTypePB.Calendar;
|
||||
}
|
||||
|
||||
class CalendarPluginConfig implements PluginConfig {
|
||||
@override
|
||||
bool get creatable => false;
|
||||
}
|
||||
|
||||
class CalendarPlugin extends Plugin {
|
||||
@override
|
||||
final ViewPluginNotifier notifier;
|
||||
final PluginType _pluginType;
|
||||
|
||||
CalendarPlugin({
|
||||
required ViewPB view,
|
||||
required PluginType pluginType,
|
||||
}) : _pluginType = pluginType,
|
||||
notifier = ViewPluginNotifier(view: view);
|
||||
|
||||
@override
|
||||
PluginDisplay get display => CalendarPluginDisplay(notifier: notifier);
|
||||
|
||||
@override
|
||||
PluginId get id => notifier.view.id;
|
||||
|
||||
@override
|
||||
PluginType get ty => _pluginType;
|
||||
}
|
||||
|
||||
class CalendarPluginDisplay extends PluginDisplay {
|
||||
final ViewPluginNotifier notifier;
|
||||
CalendarPluginDisplay({required this.notifier, Key? key});
|
||||
|
||||
ViewPB get view => notifier.view;
|
||||
|
||||
@override
|
||||
Widget get leftBarItem => ViewLeftBarItem(view: view);
|
||||
|
||||
@override
|
||||
Widget buildWidget(PluginContext context) {
|
||||
notifier.isDeleted.addListener(() {
|
||||
notifier.isDeleted.value.fold(() => null, (deletedView) {
|
||||
if (deletedView.hasIndex()) {
|
||||
context.onDeleted(view, deletedView.index);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return CalendarPage(key: ValueKey(view.id));
|
||||
// return CalendarPage(key: ValueKey(view.id), view: view);
|
||||
}
|
||||
|
||||
@override
|
||||
List<NavigationItem> get navigationItems => [this];
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:app_flowy/plugins/grid/presentation/layout/sizes.dart';
|
||||
import 'package:calendar_view/calendar_view.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
import 'layout/sizes.dart';
|
||||
import 'toolbar/calendar_toolbar.dart';
|
||||
|
||||
class CalendarPage extends StatelessWidget {
|
||||
const CalendarPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const CalendarContent();
|
||||
}
|
||||
}
|
||||
|
||||
class CalendarContent extends StatefulWidget {
|
||||
const CalendarContent({super.key});
|
||||
|
||||
@override
|
||||
State<CalendarContent> createState() => _CalendarContentState();
|
||||
}
|
||||
|
||||
class _CalendarContentState extends State<CalendarContent> {
|
||||
late EventController _eventController;
|
||||
GlobalKey<MonthViewState>? _calendarState;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_eventController = EventController();
|
||||
_calendarState = GlobalKey<MonthViewState>();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CalendarControllerProvider(
|
||||
controller: _eventController,
|
||||
child: Column(
|
||||
children: [
|
||||
// const _ToolbarBlocAdaptor(),
|
||||
_toolbar(),
|
||||
_buildCalendar(_eventController),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _toolbar() {
|
||||
return const CalendarToolbar();
|
||||
}
|
||||
|
||||
Widget _buildCalendar(EventController eventController) {
|
||||
return Expanded(
|
||||
child: MonthView(
|
||||
key: _calendarState,
|
||||
controller: _eventController,
|
||||
cellAspectRatio: 1.75,
|
||||
borderColor: Theme.of(context).dividerColor,
|
||||
headerBuilder: _headerNavigatorBuilder,
|
||||
weekDayBuilder: _headerWeekDayBuilder,
|
||||
cellBuilder: _calendarDayBuilder,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _headerNavigatorBuilder(DateTime currentMonth) {
|
||||
return Row(
|
||||
children: [
|
||||
FlowyText.medium(
|
||||
DateFormat('MMMM y', context.locale.toLanguageTag())
|
||||
.format(currentMonth),
|
||||
),
|
||||
const Spacer(),
|
||||
FlowyIconButton(
|
||||
width: CalendarSize.navigatorButtonWidth,
|
||||
height: CalendarSize.navigatorButtonHeight,
|
||||
icon: svgWidget('home/arrow_left'),
|
||||
tooltipText: LocaleKeys.calendar_navigation_previousMonth.tr(),
|
||||
hoverColor: AFThemeExtension.of(context).lightGreyHover,
|
||||
onPressed: () => _calendarState?.currentState?.previousPage(),
|
||||
),
|
||||
FlowyTextButton(
|
||||
LocaleKeys.calendar_navigation_today.tr(),
|
||||
fillColor: Colors.transparent,
|
||||
fontWeight: FontWeight.w500,
|
||||
tooltip: LocaleKeys.calendar_navigation_jumpToday.tr(),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
|
||||
hoverColor: AFThemeExtension.of(context).lightGreyHover,
|
||||
onPressed: () =>
|
||||
_calendarState?.currentState?.animateToMonth(DateTime.now()),
|
||||
),
|
||||
FlowyIconButton(
|
||||
width: CalendarSize.navigatorButtonWidth,
|
||||
height: CalendarSize.navigatorButtonHeight,
|
||||
icon: svgWidget('home/arrow_right'),
|
||||
tooltipText: LocaleKeys.calendar_navigation_nextMonth.tr(),
|
||||
hoverColor: AFThemeExtension.of(context).lightGreyHover,
|
||||
onPressed: () => _calendarState?.currentState?.nextPage(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _headerWeekDayBuilder(day) {
|
||||
final symbols = DateFormat.EEEE(context.locale.toLanguageTag()).dateSymbols;
|
||||
final weekDayString = symbols.WEEKDAYS[day];
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: CalendarSize.daysOfWeekInsets,
|
||||
child: FlowyText.medium(
|
||||
weekDayString,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _calendarDayBuilder(date, event, isToday, isInMonth) {
|
||||
Color dayTextColor = Theme.of(context).colorScheme.onSurface;
|
||||
Color cellBackgroundColor = Theme.of(context).colorScheme.surface;
|
||||
String dayString = date.day == 1
|
||||
? DateFormat('MMM d', context.locale.toLanguageTag()).format(date)
|
||||
: date.day.toString();
|
||||
|
||||
if (isToday) {
|
||||
dayTextColor = Theme.of(context).colorScheme.onPrimary;
|
||||
}
|
||||
if (!isInMonth) {
|
||||
dayTextColor = Theme.of(context).disabledColor;
|
||||
cellBackgroundColor = AFThemeExtension.of(context).lightGreyHover;
|
||||
}
|
||||
Widget day = Container(
|
||||
decoration: BoxDecoration(
|
||||
color: isToday ? Theme.of(context).colorScheme.primary : null,
|
||||
borderRadius: Corners.s6Border,
|
||||
),
|
||||
padding: GridSize.typeOptionContentInsets,
|
||||
child: FlowyText.medium(
|
||||
dayString,
|
||||
color: dayTextColor,
|
||||
),
|
||||
);
|
||||
|
||||
return Container(
|
||||
color: cellBackgroundColor,
|
||||
child: Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: day.padding(all: 6.0),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class CalendarSize {
|
||||
static double scale = 1;
|
||||
|
||||
static double get scrollBarSize => 12 * scale;
|
||||
static double get navigatorButtonWidth => 20 * scale;
|
||||
static double get navigatorButtonHeight => 25 * scale;
|
||||
static EdgeInsets get daysOfWeekInsets =>
|
||||
EdgeInsets.symmetric(vertical: 10.0 * scale);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CalendarToolbar extends StatelessWidget {
|
||||
const CalendarToolbar({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 40,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: const [
|
||||
FlowyTextButton(
|
||||
"Settings",
|
||||
fillColor: Colors.transparent,
|
||||
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,10 @@ import 'package:app_flowy/workspace/application/view/view_listener.dart';
|
||||
import 'package:app_flowy/plugins/document/application/doc_service.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart'
|
||||
show EditorState, Document, Transaction;
|
||||
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-folder/view.pb.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/trash.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||
import 'package:appflowy_backend/dispatch/dispatch.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-document/entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-document/entities.pb.dart';
|
||||
|
||||
class DocumentService {
|
||||
Future<Either<DocumentSnapshotPB, FlowyError>> openDocument({
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:app_flowy/plugins/document/application/share_service.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-document/entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:app_flowy/plugins/document/presentation/plugins/parsers/divider_node_parser.dart';
|
||||
import 'package:app_flowy/plugins/document/presentation/plugins/parsers/math_equation_node_parser.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-document/entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
@@ -48,7 +50,10 @@ class DocShareBloc extends Bloc<DocShareEvent, DocShareState> {
|
||||
String _convertDocumentToMarkdown(ExportDataPB value) {
|
||||
final json = jsonDecode(value.data);
|
||||
final document = Document.fromJson(json);
|
||||
return documentToMarkdown(document);
|
||||
return documentToMarkdown(document, customParsers: [
|
||||
const DividerNodeParser(),
|
||||
const MathEquationNodeParser(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'dart:async';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-document/protobuf.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-document/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
|
||||
class ShareService {
|
||||
Future<Either<ExportDataPB, FlowyError>> export(
|
||||
|
||||
@@ -1,29 +1,19 @@
|
||||
library document_plugin;
|
||||
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:app_flowy/plugins/document/document_page.dart';
|
||||
import 'package:app_flowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
|
||||
import 'package:app_flowy/plugins/document/presentation/more/more_button.dart';
|
||||
import 'package:app_flowy/plugins/document/presentation/share/share_button.dart';
|
||||
import 'package:app_flowy/plugins/util.dart';
|
||||
import 'package:app_flowy/startup/plugin/plugin.dart';
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/plugins/document/application/share_bloc.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/toast.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/left_bar_item.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:clipboard/clipboard.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-document/entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'document_page.dart';
|
||||
|
||||
class DocumentPluginBuilder extends PluginBuilder {
|
||||
@override
|
||||
Plugin build(dynamic data) {
|
||||
@@ -44,11 +34,13 @@ class DocumentPluginBuilder extends PluginBuilder {
|
||||
PluginType get pluginType => PluginType.editor;
|
||||
|
||||
@override
|
||||
ViewDataFormatPB get dataFormatType => ViewDataFormatPB.TreeFormat;
|
||||
ViewDataFormatPB get dataFormatType => ViewDataFormatPB.NodeFormat;
|
||||
}
|
||||
|
||||
class DocumentPlugin extends Plugin<int> {
|
||||
late PluginType _pluginType;
|
||||
final DocumentAppearanceCubit _documentAppearanceCubit =
|
||||
DocumentAppearanceCubit();
|
||||
|
||||
@override
|
||||
final ViewPluginNotifier notifier;
|
||||
@@ -59,10 +51,22 @@ class DocumentPlugin extends Plugin<int> {
|
||||
Key? key,
|
||||
}) : notifier = ViewPluginNotifier(view: view) {
|
||||
_pluginType = pluginType;
|
||||
_documentAppearanceCubit.fetch();
|
||||
}
|
||||
|
||||
@override
|
||||
PluginDisplay get display => DocumentPluginDisplay(notifier: notifier);
|
||||
void dispose() {
|
||||
_documentAppearanceCubit.close();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
PluginDisplay get display {
|
||||
return DocumentPluginDisplay(
|
||||
notifier: notifier,
|
||||
documentAppearanceCubit: _documentAppearanceCubit,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
PluginType get ty => _pluginType;
|
||||
@@ -75,8 +79,13 @@ class DocumentPluginDisplay extends PluginDisplay with NavigationItem {
|
||||
final ViewPluginNotifier notifier;
|
||||
ViewPB get view => notifier.view;
|
||||
int? deletedViewIndex;
|
||||
DocumentAppearanceCubit documentAppearanceCubit;
|
||||
|
||||
DocumentPluginDisplay({required this.notifier, Key? key});
|
||||
DocumentPluginDisplay({
|
||||
required this.notifier,
|
||||
required this.documentAppearanceCubit,
|
||||
Key? key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget buildWidget(PluginContext context) {
|
||||
@@ -88,10 +97,17 @@ class DocumentPluginDisplay extends PluginDisplay with NavigationItem {
|
||||
});
|
||||
});
|
||||
|
||||
return DocumentPage(
|
||||
view: view,
|
||||
onDeleted: () => context.onDeleted(view, deletedViewIndex),
|
||||
key: ValueKey(view.id),
|
||||
return BlocProvider.value(
|
||||
value: documentAppearanceCubit,
|
||||
child: BlocBuilder<DocumentAppearanceCubit, DocumentAppearance>(
|
||||
builder: (_, state) {
|
||||
return DocumentPage(
|
||||
view: view,
|
||||
onDeleted: () => context.onDeleted(view, deletedViewIndex),
|
||||
key: ValueKey(view.id),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -99,133 +115,19 @@ class DocumentPluginDisplay extends PluginDisplay with NavigationItem {
|
||||
Widget get leftBarItem => ViewLeftBarItem(view: view);
|
||||
|
||||
@override
|
||||
Widget? get rightBarItem => DocumentShareButton(view: view);
|
||||
Widget? get rightBarItem {
|
||||
return Row(
|
||||
children: [
|
||||
DocumentShareButton(view: view),
|
||||
const SizedBox(width: 10),
|
||||
BlocProvider.value(
|
||||
value: documentAppearanceCubit,
|
||||
child: const DocumentMoreButton(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<NavigationItem> get navigationItems => [this];
|
||||
}
|
||||
|
||||
class DocumentShareButton extends StatelessWidget {
|
||||
final ViewPB view;
|
||||
DocumentShareButton({Key? key, required this.view})
|
||||
: super(key: ValueKey(view.hashCode));
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<DocShareBloc>(param1: view),
|
||||
child: BlocListener<DocShareBloc, DocShareState>(
|
||||
listener: (context, state) {
|
||||
state.map(
|
||||
initial: (_) {},
|
||||
loading: (_) {},
|
||||
finish: (state) {
|
||||
state.successOrFail.fold(
|
||||
_handleExportData,
|
||||
_handleExportError,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: BlocBuilder<DocShareBloc, DocShareState>(
|
||||
builder: (context, state) => ConstrainedBox(
|
||||
constraints: const BoxConstraints.expand(
|
||||
height: 30,
|
||||
width: 100,
|
||||
),
|
||||
child: ShareActionList(view: view),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleExportData(ExportDataPB exportData) {
|
||||
switch (exportData.exportType) {
|
||||
case ExportType.Link:
|
||||
break;
|
||||
case ExportType.Markdown:
|
||||
FlutterClipboard.copy(exportData.data)
|
||||
.then((value) => Log.info('copied to clipboard'));
|
||||
break;
|
||||
case ExportType.Text:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _handleExportError(FlowyError error) {}
|
||||
}
|
||||
|
||||
class ShareActionList extends StatelessWidget {
|
||||
const ShareActionList({
|
||||
Key? key,
|
||||
required this.view,
|
||||
}) : super(key: key);
|
||||
|
||||
final ViewPB view;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final docShareBloc = context.read<DocShareBloc>();
|
||||
return PopoverActionList<ShareActionWrapper>(
|
||||
direction: PopoverDirection.bottomWithCenterAligned,
|
||||
actions: ShareAction.values
|
||||
.map((action) => ShareActionWrapper(action))
|
||||
.toList(),
|
||||
buildChild: (controller) {
|
||||
return RoundedTextButton(
|
||||
title: LocaleKeys.shareAction_buttonText.tr(),
|
||||
onPressed: () => controller.show(),
|
||||
);
|
||||
},
|
||||
onSelected: (action, controller) async {
|
||||
switch (action.inner) {
|
||||
case ShareAction.markdown:
|
||||
final exportPath = await FilePicker.platform.saveFile(
|
||||
dialogTitle: '',
|
||||
fileName: '${view.name}.md',
|
||||
);
|
||||
if (exportPath != null) {
|
||||
docShareBloc.add(DocShareEvent.shareMarkdown(exportPath));
|
||||
showMessageToast('Exported to: $exportPath');
|
||||
}
|
||||
break;
|
||||
case ShareAction.copyLink:
|
||||
NavigatorAlertDialog(
|
||||
title: LocaleKeys.shareAction_workInProgress.tr())
|
||||
.show(context);
|
||||
break;
|
||||
}
|
||||
controller.close();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum ShareAction {
|
||||
markdown,
|
||||
copyLink,
|
||||
}
|
||||
|
||||
class ShareActionWrapper extends ActionCell {
|
||||
final ShareAction inner;
|
||||
|
||||
ShareActionWrapper(this.inner);
|
||||
|
||||
@override
|
||||
Widget? leftIcon(Color iconColor) => null;
|
||||
|
||||
@override
|
||||
String get name => inner.name;
|
||||
}
|
||||
|
||||
extension QuestionBubbleExtension on ShareAction {
|
||||
String get name {
|
||||
switch (this) {
|
||||
case ShareAction.markdown:
|
||||
return LocaleKeys.shareAction_markdown.tr();
|
||||
case ShareAction.copyLink:
|
||||
return LocaleKeys.shareAction_copyLink.tr();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,19 @@
|
||||
import 'package:app_flowy/plugins/document/editor_styles.dart';
|
||||
import 'package:app_flowy/plugins/document/presentation/plugins/horizontal_rule_node_widget.dart';
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/plugins/document/presentation/banner.dart';
|
||||
import 'package:app_flowy/plugins/document/presentation/plugins/board/board_menu_item.dart';
|
||||
import 'package:app_flowy/plugins/document/presentation/plugins/board/board_node_widget.dart';
|
||||
import 'package:app_flowy/plugins/document/presentation/plugins/grid/grid_menu_item.dart';
|
||||
import 'package:app_flowy/plugins/document/presentation/plugins/grid/grid_node_widget.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart';
|
||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../startup/startup.dart';
|
||||
import 'application/doc_bloc.dart';
|
||||
import 'editor_styles.dart';
|
||||
import 'presentation/banner.dart';
|
||||
|
||||
class DocumentPage extends StatefulWidget {
|
||||
final VoidCallback onDeleted;
|
||||
@@ -96,15 +101,46 @@ class _DocumentPageState extends State<DocumentPage> {
|
||||
|
||||
Widget _renderAppFlowyEditor(EditorState editorState) {
|
||||
final theme = Theme.of(context);
|
||||
final editorMaxWidth = MediaQuery.of(context).size.width * 0.6;
|
||||
final editor = AppFlowyEditor(
|
||||
editorState: editorState,
|
||||
autoFocus: editorState.document.isEmpty,
|
||||
customBuilders: {
|
||||
'horizontal_rule': HorizontalRuleWidgetBuilder(),
|
||||
// Divider
|
||||
kDividerType: DividerWidgetBuilder(),
|
||||
// Math Equation
|
||||
kMathEquationType: MathEquationNodeWidgetBuidler(),
|
||||
// Code Block
|
||||
kCodeBlockType: CodeBlockNodeWidgetBuilder(),
|
||||
// Board
|
||||
kBoardType: BoardNodeWidgetBuilder(),
|
||||
// Grid
|
||||
kGridType: GridNodeWidgetBuilder(),
|
||||
// Card
|
||||
kCalloutType: CalloutNodeWidgetBuilder(),
|
||||
},
|
||||
shortcutEvents: [
|
||||
insertHorizontalRule,
|
||||
// Divider
|
||||
insertDividerEvent,
|
||||
// Code Block
|
||||
enterInCodeBlock,
|
||||
ignoreKeysInCodeBlock,
|
||||
pasteInCodeBlock,
|
||||
],
|
||||
selectionMenuItems: [
|
||||
// Divider
|
||||
dividerMenuItem,
|
||||
// Math Equation
|
||||
mathEquationMenuItem,
|
||||
// Code Block
|
||||
codeBlockMenuItem,
|
||||
// Emoji
|
||||
emojiMenuItem,
|
||||
// Board
|
||||
boardMenuItem,
|
||||
// Grid
|
||||
gridMenuItem,
|
||||
// Callout
|
||||
calloutMenuItem,
|
||||
],
|
||||
themeData: theme.copyWith(extensions: [
|
||||
...theme.extensions.values,
|
||||
@@ -115,8 +151,8 @@ class _DocumentPageState extends State<DocumentPage> {
|
||||
return Expanded(
|
||||
child: Center(
|
||||
child: Container(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: editorMaxWidth,
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: double.infinity,
|
||||
),
|
||||
child: editor,
|
||||
),
|
||||
|
||||
@@ -1,31 +1,39 @@
|
||||
import 'package:app_flowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
const _baseFontSize = 14.0;
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
EditorStyle customEditorTheme(BuildContext context) {
|
||||
final documentStyle = context.watch<DocumentAppearanceCubit>().state;
|
||||
var editorStyle = Theme.of(context).brightness == Brightness.dark
|
||||
? EditorStyle.dark
|
||||
: EditorStyle.light;
|
||||
editorStyle = editorStyle.copyWith(
|
||||
padding: const EdgeInsets.all(0),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 100, vertical: 28),
|
||||
textStyle: editorStyle.textStyle?.copyWith(
|
||||
fontFamily: 'poppins',
|
||||
fontSize: _baseFontSize,
|
||||
fontSize: documentStyle.fontSize,
|
||||
),
|
||||
placeholderTextStyle: editorStyle.placeholderTextStyle?.copyWith(
|
||||
fontFamily: 'poppins',
|
||||
fontSize: _baseFontSize,
|
||||
fontSize: documentStyle.fontSize,
|
||||
),
|
||||
bold: editorStyle.bold?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFamily: 'poppins-Bold',
|
||||
),
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
selectionMenuItemSelectedIconColor:
|
||||
Theme.of(context).textTheme.bodyMedium?.color,
|
||||
selectionMenuItemSelectedTextColor:
|
||||
Theme.of(context).textTheme.bodyMedium?.color,
|
||||
);
|
||||
return editorStyle;
|
||||
}
|
||||
|
||||
Iterable<ThemeExtension<dynamic>> customPluginTheme(BuildContext context) {
|
||||
final documentStyle = context.watch<DocumentAppearanceCubit>().state;
|
||||
final baseFontSize = documentStyle.fontSize;
|
||||
const basePadding = 12.0;
|
||||
var headingPluginStyle = Theme.of(context).brightness == Brightness.dark
|
||||
? HeadingPluginStyle.dark
|
||||
@@ -33,15 +41,15 @@ Iterable<ThemeExtension<dynamic>> customPluginTheme(BuildContext context) {
|
||||
headingPluginStyle = headingPluginStyle.copyWith(
|
||||
textStyle: (EditorState editorState, Node node) {
|
||||
final headingToFontSize = {
|
||||
'h1': _baseFontSize + 12,
|
||||
'h2': _baseFontSize + 8,
|
||||
'h3': _baseFontSize + 4,
|
||||
'h4': _baseFontSize,
|
||||
'h5': _baseFontSize,
|
||||
'h6': _baseFontSize,
|
||||
'h1': baseFontSize + 12,
|
||||
'h2': baseFontSize + 8,
|
||||
'h3': baseFontSize + 4,
|
||||
'h4': baseFontSize,
|
||||
'h5': baseFontSize,
|
||||
'h6': baseFontSize,
|
||||
};
|
||||
final fontSize =
|
||||
headingToFontSize[node.attributes.heading] ?? _baseFontSize;
|
||||
headingToFontSize[node.attributes.heading] ?? baseFontSize;
|
||||
return TextStyle(fontSize: fontSize, fontWeight: FontWeight.w600);
|
||||
},
|
||||
padding: (EditorState editorState, Node node) {
|
||||
@@ -57,10 +65,28 @@ Iterable<ThemeExtension<dynamic>> customPluginTheme(BuildContext context) {
|
||||
return EdgeInsets.only(bottom: padding);
|
||||
},
|
||||
);
|
||||
var numberListPluginStyle = Theme.of(context).brightness == Brightness.dark
|
||||
? NumberListPluginStyle.dark
|
||||
: NumberListPluginStyle.light;
|
||||
|
||||
numberListPluginStyle = numberListPluginStyle.copyWith(
|
||||
icon: (_, textNode) {
|
||||
const iconPadding = EdgeInsets.only(left: 5.0, right: 5.0);
|
||||
return Container(
|
||||
padding: iconPadding,
|
||||
child: Text(
|
||||
'${textNode.attributes.number.toString()}.',
|
||||
style: customEditorTheme(context).textStyle,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
final pluginTheme = Theme.of(context).brightness == Brightness.dark
|
||||
? darkPlguinStyleExtension
|
||||
: lightPlguinStyleExtension;
|
||||
return pluginTheme.toList()
|
||||
..removeWhere((element) => element is HeadingPluginStyle)
|
||||
..add(headingPluginStyle);
|
||||
..removeWhere((element) =>
|
||||
element is HeadingPluginStyle || element is NumberListPluginStyle)
|
||||
..add(headingPluginStyle)
|
||||
..add(numberListPluginStyle);
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
const String _kDocumentAppearenceFontSize = 'kDocumentAppearenceFontSize';
|
||||
|
||||
class DocumentAppearance {
|
||||
const DocumentAppearance({
|
||||
required this.fontSize,
|
||||
});
|
||||
|
||||
final double fontSize;
|
||||
// Will be supported...
|
||||
// final String fontName;
|
||||
|
||||
DocumentAppearance copyWith({double? fontSize}) {
|
||||
return DocumentAppearance(
|
||||
fontSize: fontSize ?? this.fontSize,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DocumentAppearanceCubit extends Cubit<DocumentAppearance> {
|
||||
DocumentAppearanceCubit() : super(const DocumentAppearance(fontSize: 14.0));
|
||||
|
||||
void fetch() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final fontSize = prefs.getDouble(_kDocumentAppearenceFontSize) ?? 14.0;
|
||||
emit(state.copyWith(
|
||||
fontSize: fontSize,
|
||||
));
|
||||
}
|
||||
|
||||
void syncFontSize(double fontSize) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
prefs.setDouble(_kDocumentAppearenceFontSize, fontSize);
|
||||
emit(state.copyWith(
|
||||
fontSize: fontSize,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import 'package:app_flowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
class FontSizeSwitcher extends StatefulWidget {
|
||||
const FontSizeSwitcher({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<FontSizeSwitcher> createState() => _FontSizeSwitcherState();
|
||||
}
|
||||
|
||||
class _FontSizeSwitcherState extends State<FontSizeSwitcher> {
|
||||
final List<Tuple3<String, double, bool>> _fontSizes = [
|
||||
Tuple3(LocaleKeys.moreAction_small.tr(), 12.0, false),
|
||||
Tuple3(LocaleKeys.moreAction_medium.tr(), 14.0, true),
|
||||
Tuple3(LocaleKeys.moreAction_large.tr(), 18.0, false),
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<DocumentAppearanceCubit, DocumentAppearance>(
|
||||
builder: (context, state) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
FlowyText.semibold(
|
||||
LocaleKeys.moreAction_fontSize.tr(),
|
||||
fontSize: 12,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
ToggleButtons(
|
||||
isSelected:
|
||||
_fontSizes.map((e) => e.item2 == state.fontSize).toList(),
|
||||
onPressed: (int index) {
|
||||
_updateSelectedFontSize(_fontSizes[index].item2);
|
||||
},
|
||||
borderRadius: const BorderRadius.all(Radius.circular(5)),
|
||||
selectedBorderColor: Theme.of(context).colorScheme.primaryContainer,
|
||||
selectedColor: Theme.of(context).colorScheme.onSurface,
|
||||
fillColor: Theme.of(context).colorScheme.primaryContainer,
|
||||
color: Theme.of(context).hintColor,
|
||||
constraints: const BoxConstraints(
|
||||
minHeight: 40.0,
|
||||
minWidth: 80.0,
|
||||
),
|
||||
children: _fontSizes
|
||||
.map((e) => Text(
|
||||
e.item1,
|
||||
style: TextStyle(fontSize: e.item2),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void _updateSelectedFontSize(double fontSize) {
|
||||
context.read<DocumentAppearanceCubit>().syncFontSize(fontSize);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import 'package:app_flowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
|
||||
import 'package:app_flowy/plugins/document/presentation/more/font_size_switcher.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class DocumentMoreButton extends StatelessWidget {
|
||||
const DocumentMoreButton({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PopupMenuButton<int>(
|
||||
offset: const Offset(0, 30),
|
||||
itemBuilder: (context) {
|
||||
return [
|
||||
PopupMenuItem(
|
||||
value: 1,
|
||||
enabled: false,
|
||||
child: BlocProvider.value(
|
||||
value: context.read<DocumentAppearanceCubit>(),
|
||||
child: const FontSizeSwitcher(),
|
||||
),
|
||||
),
|
||||
];
|
||||
},
|
||||
child: svgWidget(
|
||||
'editor/details',
|
||||
size: const Size(18, 18),
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
import 'package:app_flowy/plugins/document/presentation/plugins/base/insert_page_command.dart';
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/application/app/app_service.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pbserver.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:dartz/dartz.dart' as dartz;
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:app_flowy/workspace/application/view/view_ext.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/menu/menu.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
|
||||
class BuiltInPageWidget extends StatefulWidget {
|
||||
const BuiltInPageWidget({
|
||||
Key? key,
|
||||
required this.node,
|
||||
required this.editorState,
|
||||
required this.builder,
|
||||
}) : super(key: key);
|
||||
|
||||
final Node node;
|
||||
final EditorState editorState;
|
||||
final Widget Function(ViewPB viewPB) builder;
|
||||
|
||||
@override
|
||||
State<BuiltInPageWidget> createState() => _BuiltInPageWidgetState();
|
||||
}
|
||||
|
||||
class _BuiltInPageWidgetState extends State<BuiltInPageWidget> {
|
||||
final focusNode = FocusNode();
|
||||
|
||||
String get gridID {
|
||||
return widget.node.attributes[kViewID];
|
||||
}
|
||||
|
||||
String get appID {
|
||||
return widget.node.attributes[kAppID];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<dartz.Either<ViewPB, FlowyError>>(
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
final board = snapshot.data?.getLeftOrNull<ViewPB>();
|
||||
if (board != null) {
|
||||
return _build(context, board);
|
||||
}
|
||||
}
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
},
|
||||
future: AppService().getView(appID, gridID),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Widget _build(BuildContext context, ViewPB viewPB) {
|
||||
return MouseRegion(
|
||||
onEnter: (event) {
|
||||
widget.editorState.service.scrollService?.disable();
|
||||
},
|
||||
onExit: (event) {
|
||||
widget.editorState.service.scrollService?.enable();
|
||||
},
|
||||
child: SizedBox(
|
||||
height: 400,
|
||||
child: Stack(
|
||||
children: [
|
||||
_buildMenu(context, viewPB),
|
||||
_buildGrid(context, viewPB),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildGrid(BuildContext context, ViewPB viewPB) {
|
||||
return Focus(
|
||||
focusNode: focusNode,
|
||||
onFocusChange: (value) {
|
||||
if (value) {
|
||||
widget.editorState.service.selectionService.clearSelection();
|
||||
}
|
||||
},
|
||||
child: widget.builder(viewPB),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMenu(BuildContext context, ViewPB viewPB) {
|
||||
return Positioned(
|
||||
top: 5,
|
||||
left: 5,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// information
|
||||
FlowyIconButton(
|
||||
tooltipText: LocaleKeys.tooltip_referencePage.tr(namedArgs: {
|
||||
'name': viewPB.layout.name,
|
||||
}),
|
||||
width: 24,
|
||||
height: 24,
|
||||
iconPadding: const EdgeInsets.all(3),
|
||||
icon: svgWidget('common/information'),
|
||||
),
|
||||
// Name
|
||||
const Space(7, 0),
|
||||
FlowyText.medium(
|
||||
viewPB.name,
|
||||
fontSize: 16.0,
|
||||
),
|
||||
// setting
|
||||
const Space(7, 0),
|
||||
PopoverActionList<_ActionWrapper>(
|
||||
direction: PopoverDirection.bottomWithCenterAligned,
|
||||
actions: _ActionType.values
|
||||
.map((action) => _ActionWrapper(action))
|
||||
.toList(),
|
||||
buildChild: (controller) {
|
||||
return FlowyIconButton(
|
||||
tooltipText: LocaleKeys.tooltip_openMenu.tr(),
|
||||
width: 24,
|
||||
height: 24,
|
||||
iconPadding: const EdgeInsets.all(3),
|
||||
icon: svgWidget('common/settings'),
|
||||
onPressed: () => controller.show(),
|
||||
);
|
||||
},
|
||||
onSelected: (action, controller) async {
|
||||
switch (action.inner) {
|
||||
case _ActionType.viewDatabase:
|
||||
getIt<MenuSharedState>().latestOpenView = viewPB;
|
||||
getIt<HomeStackManager>().setPlugin(viewPB.plugin());
|
||||
break;
|
||||
case _ActionType.delete:
|
||||
final transaction = widget.editorState.transaction;
|
||||
transaction.deleteNode(widget.node);
|
||||
widget.editorState.apply(transaction);
|
||||
break;
|
||||
}
|
||||
controller.close();
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum _ActionType {
|
||||
viewDatabase,
|
||||
delete,
|
||||
}
|
||||
|
||||
class _ActionWrapper extends ActionCell {
|
||||
final _ActionType inner;
|
||||
|
||||
_ActionWrapper(this.inner);
|
||||
|
||||
Widget? icon(Color iconColor) => null;
|
||||
|
||||
@override
|
||||
String get name {
|
||||
switch (inner) {
|
||||
case _ActionType.viewDatabase:
|
||||
return LocaleKeys.tooltip_viewDataBase.tr();
|
||||
case _ActionType.delete:
|
||||
return LocaleKeys.disclosureAction_delete.tr();
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import 'package:app_flowy/plugins/document/presentation/plugins/board/board_node_widget.dart';
|
||||
import 'package:app_flowy/plugins/document/presentation/plugins/grid/grid_node_widget.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/app.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
|
||||
const String kAppID = 'app_id';
|
||||
const String kViewID = 'view_id';
|
||||
|
||||
extension InsertPage on EditorState {
|
||||
void insertPage(AppPB appPB, ViewPB viewPB) {
|
||||
final selection = service.selectionService.currentSelection.value;
|
||||
final textNodes =
|
||||
service.selectionService.currentSelectedNodes.whereType<TextNode>();
|
||||
if (selection == null || textNodes.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final transaction = this.transaction;
|
||||
transaction.insertNode(
|
||||
selection.end.path,
|
||||
Node(
|
||||
type: _convertPageType(viewPB),
|
||||
attributes: {
|
||||
kAppID: appPB.id,
|
||||
kViewID: viewPB.id,
|
||||
},
|
||||
),
|
||||
);
|
||||
apply(transaction);
|
||||
}
|
||||
|
||||
String _convertPageType(ViewPB viewPB) {
|
||||
switch (viewPB.layout) {
|
||||
case ViewLayoutTypePB.Grid:
|
||||
return kGridType;
|
||||
case ViewLayoutTypePB.Board:
|
||||
return kBoardType;
|
||||
default:
|
||||
throw Exception('Unknown layout type');
|
||||
}
|
||||
}
|
||||
}
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
import 'package:app_flowy/workspace/application/app/app_service.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/app.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:dartz/dartz.dart' as dartz;
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'insert_page_command.dart';
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
EditorState? _editorState;
|
||||
OverlayEntry? _linkToPageMenu;
|
||||
|
||||
void showLinkToPageMenu(
|
||||
EditorState editorState,
|
||||
SelectionMenuService menuService,
|
||||
BuildContext context,
|
||||
ViewLayoutTypePB pageType,
|
||||
) {
|
||||
final aligment = menuService.alignment;
|
||||
final offset = menuService.offset;
|
||||
menuService.dismiss();
|
||||
|
||||
_editorState = editorState;
|
||||
|
||||
String hintText = '';
|
||||
switch (pageType) {
|
||||
case ViewLayoutTypePB.Grid:
|
||||
hintText = LocaleKeys.document_slashMenu_grid_selectAGridToLinkTo.tr();
|
||||
break;
|
||||
case ViewLayoutTypePB.Board:
|
||||
hintText = LocaleKeys.document_slashMenu_board_selectABoardToLinkTo.tr();
|
||||
break;
|
||||
default:
|
||||
throw Exception('Unknown layout type');
|
||||
}
|
||||
|
||||
_linkToPageMenu?.remove();
|
||||
_linkToPageMenu = OverlayEntry(builder: (context) {
|
||||
return Positioned(
|
||||
top: aligment == Alignment.bottomLeft ? offset.dy : null,
|
||||
bottom: aligment == Alignment.topLeft ? offset.dy : null,
|
||||
left: offset.dx,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: LinkToPageMenu(
|
||||
editorState: editorState,
|
||||
layoutType: pageType,
|
||||
hintText: hintText,
|
||||
onSelected: (appPB, viewPB) {
|
||||
editorState.insertPage(appPB, viewPB);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
Overlay.of(context)?.insert(_linkToPageMenu!);
|
||||
|
||||
editorState.service.selectionService.currentSelection
|
||||
.addListener(dismissLinkToPageMenu);
|
||||
}
|
||||
|
||||
void dismissLinkToPageMenu() {
|
||||
_linkToPageMenu?.remove();
|
||||
_linkToPageMenu = null;
|
||||
|
||||
_editorState?.service.selectionService.currentSelection
|
||||
.removeListener(dismissLinkToPageMenu);
|
||||
_editorState = null;
|
||||
}
|
||||
|
||||
class LinkToPageMenu extends StatefulWidget {
|
||||
const LinkToPageMenu({
|
||||
super.key,
|
||||
required this.editorState,
|
||||
required this.layoutType,
|
||||
required this.hintText,
|
||||
required this.onSelected,
|
||||
});
|
||||
|
||||
final EditorState editorState;
|
||||
final ViewLayoutTypePB layoutType;
|
||||
final String hintText;
|
||||
final void Function(AppPB appPB, ViewPB viewPB) onSelected;
|
||||
|
||||
@override
|
||||
State<LinkToPageMenu> createState() => _LinkToPageMenuState();
|
||||
}
|
||||
|
||||
class _LinkToPageMenuState extends State<LinkToPageMenu> {
|
||||
EditorStyle get style => widget.editorState.editorStyle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
color: Colors.transparent,
|
||||
width: 300,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.fromLTRB(10, 6, 10, 6),
|
||||
decoration: BoxDecoration(
|
||||
color: style.selectionMenuBackgroundColor,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
blurRadius: 5,
|
||||
spreadRadius: 1,
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
),
|
||||
],
|
||||
borderRadius: BorderRadius.circular(6.0),
|
||||
),
|
||||
child: _buildListWidget(context),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildListWidget(BuildContext context) {
|
||||
return FutureBuilder<List<dartz.Tuple2<AppPB, List<ViewPB>>>>(
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData &&
|
||||
snapshot.connectionState == ConnectionState.done) {
|
||||
final apps = snapshot.data;
|
||||
final children = <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: FlowyText.regular(
|
||||
widget.hintText,
|
||||
fontSize: 10,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
];
|
||||
if (apps != null && apps.isNotEmpty) {
|
||||
for (final app in apps) {
|
||||
if (app.value2.isNotEmpty) {
|
||||
children.add(
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: FlowyText.regular(
|
||||
app.value1.name,
|
||||
),
|
||||
),
|
||||
);
|
||||
for (final value in app.value2) {
|
||||
children.add(
|
||||
FlowyButton(
|
||||
leftIcon: svgWidget(
|
||||
_iconName(value),
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
text: FlowyText.regular(value.name),
|
||||
onTap: () => widget.onSelected(app.value1, value),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: children,
|
||||
);
|
||||
} else {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
},
|
||||
future: AppService().fetchViews(widget.layoutType),
|
||||
);
|
||||
}
|
||||
|
||||
String _iconName(ViewPB viewPB) {
|
||||
switch (viewPB.layout) {
|
||||
case ViewLayoutTypePB.Grid:
|
||||
return 'editor/grid';
|
||||
case ViewLayoutTypePB.Board:
|
||||
return 'editor/board';
|
||||
default:
|
||||
throw Exception('Unknown layout type');
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:app_flowy/plugins/document/presentation/plugins/base/link_to_page_widget.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
SelectionMenuItem boardMenuItem = SelectionMenuItem(
|
||||
name: () => LocaleKeys.document_plugins_referencedBoard.tr(),
|
||||
icon: (editorState, onSelected) {
|
||||
return svgWidget(
|
||||
'editor/board',
|
||||
size: const Size.square(18.0),
|
||||
color: onSelected
|
||||
? editorState.editorStyle.selectionMenuItemSelectedIconColor
|
||||
: editorState.editorStyle.selectionMenuItemIconColor,
|
||||
);
|
||||
},
|
||||
keywords: ['board', 'kanban'],
|
||||
handler: (editorState, menuService, context) {
|
||||
showLinkToPageMenu(
|
||||
editorState,
|
||||
menuService,
|
||||
context,
|
||||
ViewLayoutTypePB.Board,
|
||||
);
|
||||
},
|
||||
);
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
import 'package:app_flowy/plugins/board/presentation/board_page.dart';
|
||||
import 'package:app_flowy/plugins/document/presentation/plugins/base/built_in_page_widget.dart';
|
||||
import 'package:app_flowy/plugins/document/presentation/plugins/base/insert_page_command.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
const String kBoardType = 'board';
|
||||
|
||||
class BoardNodeWidgetBuilder extends NodeWidgetBuilder<Node> {
|
||||
@override
|
||||
Widget build(NodeWidgetContext<Node> context) {
|
||||
return _BoardWidget(
|
||||
key: context.node.key,
|
||||
node: context.node,
|
||||
editorState: context.editorState,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
NodeValidator<Node> get nodeValidator => (node) {
|
||||
return node.attributes[kViewID] is String &&
|
||||
node.attributes[kAppID] is String;
|
||||
};
|
||||
}
|
||||
|
||||
class _BoardWidget extends StatefulWidget {
|
||||
const _BoardWidget({
|
||||
Key? key,
|
||||
required this.node,
|
||||
required this.editorState,
|
||||
}) : super(key: key);
|
||||
|
||||
final Node node;
|
||||
final EditorState editorState;
|
||||
|
||||
@override
|
||||
State<_BoardWidget> createState() => _BoardWidgetState();
|
||||
}
|
||||
|
||||
class _BoardWidgetState extends State<_BoardWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BuiltInPageWidget(
|
||||
node: widget.node,
|
||||
editorState: widget.editorState,
|
||||
builder: (viewPB) {
|
||||
return BoardPage(
|
||||
key: ValueKey(viewPB.id),
|
||||
view: viewPB,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:app_flowy/plugins/document/presentation/plugins/base/link_to_page_widget.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
SelectionMenuItem gridMenuItem = SelectionMenuItem(
|
||||
name: () => LocaleKeys.document_plugins_referencedGrid.tr(),
|
||||
icon: (editorState, onSelected) {
|
||||
return svgWidget(
|
||||
'editor/grid',
|
||||
size: const Size.square(18.0),
|
||||
color: onSelected
|
||||
? editorState.editorStyle.selectionMenuItemSelectedIconColor
|
||||
: editorState.editorStyle.selectionMenuItemIconColor,
|
||||
);
|
||||
},
|
||||
keywords: ['grid'],
|
||||
handler: (editorState, menuService, context) {
|
||||
showLinkToPageMenu(
|
||||
editorState,
|
||||
menuService,
|
||||
context,
|
||||
ViewLayoutTypePB.Grid,
|
||||
);
|
||||
},
|
||||
);
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
import 'package:app_flowy/plugins/document/presentation/plugins/base/built_in_page_widget.dart';
|
||||
import 'package:app_flowy/plugins/document/presentation/plugins/base/insert_page_command.dart';
|
||||
import 'package:app_flowy/plugins/grid/presentation/grid_page.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
const String kGridType = 'grid';
|
||||
|
||||
class GridNodeWidgetBuilder extends NodeWidgetBuilder<Node> {
|
||||
@override
|
||||
Widget build(NodeWidgetContext<Node> context) {
|
||||
return _GridWidget(
|
||||
key: context.node.key,
|
||||
node: context.node,
|
||||
editorState: context.editorState,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
NodeValidator<Node> get nodeValidator => (node) {
|
||||
return node.attributes[kAppID] is String &&
|
||||
node.attributes[kViewID] is String;
|
||||
};
|
||||
}
|
||||
|
||||
class _GridWidget extends StatefulWidget {
|
||||
const _GridWidget({
|
||||
Key? key,
|
||||
required this.node,
|
||||
required this.editorState,
|
||||
}) : super(key: key);
|
||||
|
||||
final Node node;
|
||||
final EditorState editorState;
|
||||
|
||||
@override
|
||||
State<_GridWidget> createState() => _GridWidgetState();
|
||||
}
|
||||
|
||||
class _GridWidgetState extends State<_GridWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BuiltInPageWidget(
|
||||
node: widget.node,
|
||||
editorState: widget.editorState,
|
||||
builder: (viewPB) {
|
||||
return GridPage(
|
||||
key: ValueKey(viewPB.id),
|
||||
view: viewPB,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
|
||||
class DividerNodeParser extends NodeParser {
|
||||
const DividerNodeParser();
|
||||
|
||||
@override
|
||||
String get id => 'divider';
|
||||
|
||||
@override
|
||||
String transform(Node node) {
|
||||
return '---\n';
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
|
||||
class MathEquationNodeParser extends NodeParser {
|
||||
const MathEquationNodeParser();
|
||||
|
||||
@override
|
||||
String get id => 'math_equation';
|
||||
|
||||
@override
|
||||
String transform(Node node) {
|
||||
return '\$\$${node.attributes[id]}\$\$';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
library document_plugin;
|
||||
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/plugins/document/application/share_bloc.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/toast.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:clipboard/clipboard.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-document/entities.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class DocumentShareButton extends StatelessWidget {
|
||||
final ViewPB view;
|
||||
DocumentShareButton({Key? key, required this.view})
|
||||
: super(key: ValueKey(view.hashCode));
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<DocShareBloc>(param1: view),
|
||||
child: BlocListener<DocShareBloc, DocShareState>(
|
||||
listener: (context, state) {
|
||||
state.map(
|
||||
initial: (_) {},
|
||||
loading: (_) {},
|
||||
finish: (state) {
|
||||
state.successOrFail.fold(
|
||||
_handleExportData,
|
||||
_handleExportError,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: BlocBuilder<DocShareBloc, DocShareState>(
|
||||
builder: (context, state) => ConstrainedBox(
|
||||
constraints: const BoxConstraints.expand(
|
||||
height: 30,
|
||||
width: 100,
|
||||
),
|
||||
child: ShareActionList(view: view),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleExportData(ExportDataPB exportData) {
|
||||
switch (exportData.exportType) {
|
||||
case ExportType.Link:
|
||||
break;
|
||||
case ExportType.Markdown:
|
||||
FlutterClipboard.copy(exportData.data)
|
||||
.then((value) => Log.info('copied to clipboard'));
|
||||
break;
|
||||
case ExportType.Text:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _handleExportError(FlowyError error) {}
|
||||
}
|
||||
|
||||
class ShareActionList extends StatelessWidget {
|
||||
const ShareActionList({
|
||||
Key? key,
|
||||
required this.view,
|
||||
}) : super(key: key);
|
||||
|
||||
final ViewPB view;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final docShareBloc = context.read<DocShareBloc>();
|
||||
return PopoverActionList<ShareActionWrapper>(
|
||||
direction: PopoverDirection.bottomWithCenterAligned,
|
||||
actions: ShareAction.values
|
||||
.map((action) => ShareActionWrapper(action))
|
||||
.toList(),
|
||||
buildChild: (controller) {
|
||||
return RoundedTextButton(
|
||||
title: LocaleKeys.shareAction_buttonText.tr(),
|
||||
onPressed: () => controller.show(),
|
||||
);
|
||||
},
|
||||
onSelected: (action, controller) async {
|
||||
switch (action.inner) {
|
||||
case ShareAction.markdown:
|
||||
final exportPath = await FilePicker.platform.saveFile(
|
||||
dialogTitle: '',
|
||||
fileName: '${view.name}.md',
|
||||
);
|
||||
if (exportPath != null) {
|
||||
docShareBloc.add(DocShareEvent.shareMarkdown(exportPath));
|
||||
showMessageToast('Exported to: $exportPath');
|
||||
}
|
||||
break;
|
||||
case ShareAction.copyLink:
|
||||
NavigatorAlertDialog(
|
||||
title: LocaleKeys.shareAction_workInProgress.tr(),
|
||||
).show(context);
|
||||
break;
|
||||
}
|
||||
controller.close();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum ShareAction {
|
||||
markdown,
|
||||
copyLink,
|
||||
}
|
||||
|
||||
class ShareActionWrapper extends ActionCell {
|
||||
final ShareAction inner;
|
||||
|
||||
ShareActionWrapper(this.inner);
|
||||
|
||||
Widget? icon(Color iconColor) => null;
|
||||
|
||||
@override
|
||||
String get name {
|
||||
switch (inner) {
|
||||
case ShareAction.markdown:
|
||||
return LocaleKeys.shareAction_markdown.tr();
|
||||
case ShareAction.copyLink:
|
||||
return LocaleKeys.shareAction_copyLink.tr();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||
|
||||
import '../field/field_controller.dart';
|
||||
import '../row/row_cache.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 BlockPB block;
|
||||
late GridRowCache _rowCache;
|
||||
late GridBlockListener _listener;
|
||||
|
||||
List<RowInfo> get rows => _rowCache.visibleRows;
|
||||
GridRowCache get rowCache => _rowCache;
|
||||
|
||||
GridBlockCache({
|
||||
required this.gridId,
|
||||
required this.block,
|
||||
required GridFieldController fieldController,
|
||||
}) {
|
||||
_rowCache = GridRowCache(
|
||||
gridId: gridId,
|
||||
block: block,
|
||||
notifier: GridRowFieldNotifierImpl(fieldController),
|
||||
);
|
||||
|
||||
_listener = GridBlockListener(blockId: block.id);
|
||||
_listener.start((result) {
|
||||
result.fold(
|
||||
(changeset) => _rowCache.applyChangesets(changeset),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
await _listener.stop();
|
||||
await _rowCache.dispose();
|
||||
}
|
||||
|
||||
void addListener({
|
||||
required void Function(RowsChangedReason) onRowsChanged,
|
||||
bool Function()? listenWhen,
|
||||
}) {
|
||||
_rowCache.onRowsChanged((reason) {
|
||||
if (listenWhen != null && listenWhen() == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
onRowsChanged(reason);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
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<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(GridDartNotification ty, Either<Uint8List, FlowyError> result) {
|
||||
switch (ty) {
|
||||
case GridDartNotification.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,7 +1,7 @@
|
||||
import 'package:app_flowy/core/grid_notification.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:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database/notification.pb.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
@@ -13,18 +13,18 @@ class CellListener {
|
||||
final String fieldId;
|
||||
PublishNotifier<UpdateFieldNotifiedValue>? _updateCellNotifier =
|
||||
PublishNotifier();
|
||||
GridNotificationListener? _listener;
|
||||
DatabaseNotificationListener? _listener;
|
||||
CellListener({required this.rowId, required this.fieldId});
|
||||
|
||||
void start({required void Function(UpdateFieldNotifiedValue) onCellChanged}) {
|
||||
_updateCellNotifier?.addPublishListener(onCellChanged);
|
||||
_listener = GridNotificationListener(
|
||||
_listener = DatabaseNotificationListener(
|
||||
objectId: "$rowId:$fieldId", handler: _handler);
|
||||
}
|
||||
|
||||
void _handler(GridDartNotification ty, Either<Uint8List, FlowyError> result) {
|
||||
void _handler(DatabaseNotification ty, Either<Uint8List, FlowyError> result) {
|
||||
switch (ty) {
|
||||
case GridDartNotification.DidUpdateCell:
|
||||
case DatabaseNotification.DidUpdateCell:
|
||||
result.fold(
|
||||
(payload) => _updateCellNotifier?.value = left(unit),
|
||||
(error) => _updateCellNotifier?.value = right(error),
|
||||
|
||||
Loaded 100 of 1551 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user