Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
565485265b | ||
|
|
f6f615a54c | ||
|
|
17d88f2ad3 | ||
|
|
8f66246e25 | ||
|
|
063ec186bb | ||
|
|
ec444d4a00 | ||
|
|
95475cb870 | ||
|
|
0520855925 | ||
|
|
61704f6d4a | ||
|
|
b85990e453 | ||
|
|
b0d525a48f | ||
|
|
bf048ffc0d | ||
|
|
1f44d4de33 | ||
|
|
653f88033e | ||
|
|
0c88c7ce2f | ||
|
|
f38688b17e | ||
|
|
1a3701feaf | ||
|
|
1b86587875 | ||
|
|
120db6805f | ||
|
|
6b14983f2b | ||
|
|
9a4cde07b6 | ||
|
|
d6d591f359 | ||
|
|
0570615414 | ||
|
|
609bf2a4ce | ||
|
|
75231e606c | ||
|
|
dd9c3dd2a3 | ||
|
|
4821d7ea65 | ||
|
|
874d1e86c2 | ||
|
|
b7ec7ce32e | ||
|
|
0141538be8 | ||
|
|
dcf48ceb50 | ||
|
|
972ef2149c | ||
|
|
668e1196d1 | ||
|
|
688d55e00f | ||
|
|
40c23b7c7d | ||
|
|
5b4043b805 | ||
|
|
2368f5dc4a | ||
|
|
0dac41b114 | ||
|
|
b89c69f294 | ||
|
|
77ff2e987a | ||
|
|
5e8f6a53a0 | ||
|
|
1d28bed281 | ||
|
|
21199c04ac | ||
|
|
d3823eb076 | ||
|
|
7106195d8a | ||
|
|
90da54d12f | ||
|
|
e73870e6e2 | ||
|
|
9e235c578e | ||
|
|
7ff4cecd09 | ||
|
|
675c833f07 | ||
|
|
d3ee346cb7 | ||
|
|
99f0fad74c | ||
|
|
45045beeb9 | ||
|
|
7e7cee4bf4 | ||
|
|
8e22ef2230 | ||
|
|
6aa9ba28d6 | ||
|
|
205b0fc4a5 | ||
|
|
3f0d3d802a | ||
|
|
0f3c6d6a04 | ||
|
|
59a1910b3c | ||
|
|
f1316acfcc | ||
|
|
fd41459a30 | ||
|
|
a1a5675875 | ||
|
|
f6957fb160 | ||
|
|
085ef8f668 | ||
|
|
1945b0fe05 | ||
|
|
2451d3188c | ||
|
|
4a81fed6e4 | ||
|
|
bf8e36a08b | ||
|
|
61fd608200 | ||
|
|
6877607c5e | ||
|
|
01a388c1c4 | ||
|
|
f4dea31560 | ||
|
|
b356354cd8 | ||
|
|
f76d722b4c | ||
|
|
346a09b2ae | ||
|
|
3fd4371d2c | ||
|
|
8a2f5fe789 | ||
|
|
7c3a823078 | ||
|
|
2f9823d12a | ||
|
|
ea9d8d03ad | ||
|
|
64902f763b | ||
|
|
c1de3ba01b | ||
|
|
72e155f5b9 | ||
|
|
7207e35349 | ||
|
|
51041f6860 | ||
|
|
59cb4a890a | ||
|
|
91b942d319 | ||
|
|
9c8753b191 | ||
|
|
2f803959e7 | ||
|
|
592b918175 | ||
|
|
6bb1c4e89c | ||
|
|
e2496e734c | ||
|
|
6c895ad4fe | ||
|
|
8588afcda6 | ||
|
|
1ad08ba59d | ||
|
|
cbd351453d | ||
|
|
72768a2f77 | ||
|
|
6b8c7d7bc6 | ||
|
|
84ea888c73 | ||
|
|
95ec607482 | ||
|
|
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
+1
-1
@@ -1,4 +1,4 @@
|
||||
frontend/app_flowy/
|
||||
frontend/appflowy_flutter/
|
||||
frontend/scripts/
|
||||
frontend/rust-lib/target
|
||||
shared-lib/target/
|
||||
@@ -11,10 +11,11 @@ on:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/app_flowy/packages/appflowy_editor/**"
|
||||
- "frontend/appflowy_flutter/packages/appflowy_editor/**"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
FLUTTER_VERSION: "3.3.9"
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
@@ -30,11 +31,11 @@ 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
|
||||
working-directory: frontend/app_flowy/packages/appflowy_editor
|
||||
working-directory: frontend/appflowy_flutter/packages/appflowy_editor
|
||||
run: |
|
||||
flutter pub get
|
||||
flutter format --set-exit-if-changed .
|
||||
@@ -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/appflowy_flutter
|
||||
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/appflowy_flutter/**"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/appflowy_flutter/**"
|
||||
|
||||
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/appflowy_flutter
|
||||
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/appflowy_flutter
|
||||
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
|
||||
|
||||
+278
-195
@@ -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
|
||||
build-for-windows:
|
||||
name: ${{ matrix.job.target }} (${{ matrix.job.os }})
|
||||
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
|
||||
WINDOWS_APP_RELEASE_PATH: frontend\appflowy_flutter\product\${{ github.ref_name }}\windows
|
||||
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
|
||||
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
|
||||
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
|
||||
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,242 @@ 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/appflowy_flutter/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/appflowy_flutter/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
|
||||
|
||||
notify-discord:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-for-linux, build-for-windows, build-for-macOS]
|
||||
steps:
|
||||
- name: Notify Discord
|
||||
run: |
|
||||
curl -H "Content-Type: application/json" -d '{"username": "release@appflowy", "content": "🎉 AppFlowy ${{ github.ref_name }} is available. https://github.com/LucasXu0/AppFlowy/releases/tag/'${{ github.ref_name }}'"}' "https://discord.com/api/webhooks/${{ secrets.DISCORD }}"
|
||||
shell: bash
|
||||
@@ -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
|
||||
@@ -0,0 +1,82 @@
|
||||
name: Tauri-CI
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "frontend/rust-lib/**"
|
||||
- "frontend/appflowy_tauri/**"
|
||||
|
||||
env:
|
||||
NODE_VERSION: "16"
|
||||
RUST_TOOLCHAIN: "1.65"
|
||||
|
||||
jobs:
|
||||
tauri-build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# platform: [macos-latest, ubuntu-20.04, windows-latest]
|
||||
platform: [macos-latest]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: ${{ matrix.platform }}
|
||||
|
||||
- name: install dependencies (windows only)
|
||||
if: matrix.platform == 'windows-latest'
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo install --force cargo-make
|
||||
cargo install --force duckscript_cli
|
||||
vcpkg integrate install
|
||||
cargo make appflowy-tauri-deps-tools
|
||||
|
||||
- name: install dependencies (ubuntu only)
|
||||
if: matrix.platform == 'ubuntu-20.04'
|
||||
working-directory: frontend
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
|
||||
cargo install --force cargo-make
|
||||
cargo make appflowy-tauri-deps-tools
|
||||
|
||||
- name: install dependencies (macOS only)
|
||||
if: matrix.platform == 'macos-latest'
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo install --force cargo-make
|
||||
cargo make appflowy-tauri-deps-tools
|
||||
|
||||
# - name: lint
|
||||
# working-directory: frontend/appflowy_tauri
|
||||
# run: |
|
||||
# yarn --frozen-lockfile
|
||||
# yarn test:prettier
|
||||
# yarn test:code
|
||||
|
||||
- name: build
|
||||
working-directory: frontend/appflowy_tauri
|
||||
run: |
|
||||
mkdir dist
|
||||
npm install
|
||||
cargo make --cwd .. tauri_build
|
||||
yarn && yarn build
|
||||
|
||||
- uses: tauri-apps/tauri-action@v0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -3,7 +3,7 @@ on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- "frontend/app_flowy/assets/translations/en.json"
|
||||
- "frontend/appflowy_flutter/assets/translations/en.json"
|
||||
|
||||
jobs:
|
||||
Discord-Notify:
|
||||
|
||||
+2
-3
@@ -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
|
||||
@@ -31,8 +29,9 @@ frontend/.vscode/*
|
||||
|
||||
# Commit the highest level pubspec.lock, but ignore the others
|
||||
pubspec.lock
|
||||
!frontend/app_flowy/pubspec.lock
|
||||
!frontend/appflowy_flutter/pubspec.lock
|
||||
|
||||
# ignore tool used for commit linting
|
||||
.githooks/gitlint
|
||||
.githooks/gitlint.exe
|
||||
.fvm/
|
||||
@@ -10,7 +10,7 @@
|
||||
<env name="flowy_tool" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/scripts/flowy-tool/Cargo.toml" />
|
||||
<env name="rust_lib" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/" />
|
||||
<env name="shared_lib" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/../shared_lib" />
|
||||
<env name="flutter_lib" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/app_flowy/packages" />
|
||||
<env name="flutter_lib" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/appflowy_flutter/packages" />
|
||||
<env name="derive_meta" value="${shared_lib}/flowy-derive/src/derive_cache/derive_cache.rs" />
|
||||
<env name="flutter_package_lib" value="${flutter_lib}/flowy_sdk/lib" />
|
||||
</envs>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="dart-event" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="run --manifest-path $PROJECT_DIR$/scripts/flowy-tool/Cargo.toml -- dart-event --rust_source=$PROJECT_DIR$/rust-lib/ --output=$PROJECT_DIR$/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event.dart" />
|
||||
<option name="command" value="run --manifest-path $PROJECT_DIR$/scripts/flowy-tool/Cargo.toml -- dart-event --rust_source=$PROJECT_DIR$/rust-lib/ --output=$PROJECT_DIR$/appflowy_flutter/packages/flowy_sdk/lib/dispatch/dart_event.dart" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="allFeatures" value="false" />
|
||||
|
||||
+124
-24
@@ -1,58 +1,150 @@
|
||||
# Release Notes
|
||||
## Version 0.0.7 - 11/27/2022
|
||||
|
||||
## Version 0.1.2 - 03/28/2023
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: update calendar selected range.
|
||||
- Fix: duplicate view.
|
||||
|
||||
## Version 0.1.1 - 03/21/2023
|
||||
|
||||
### New features
|
||||
|
||||
- AppFlowy brings the power of OpenAI into your AppFlowy pages. Ask AI to write anything for you in AppFlowy.
|
||||
- Support adding a cover image to your page, making your pages beautiful.
|
||||
- More shortcuts become available. Click on '?' at the bottom right to access our shortcut guide.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix some bugs
|
||||
|
||||
## 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 +154,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 +162,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 +194,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 +207,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
+58
-54
@@ -6,51 +6,34 @@
|
||||
"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}"
|
||||
"cwd": "${workspaceRoot}/appflowy_flutter"
|
||||
},
|
||||
{
|
||||
// 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",
|
||||
"env": {
|
||||
"RUST_LOG": "debug"
|
||||
},
|
||||
"cwd": "${workspaceRoot}/app_flowy"
|
||||
"cwd": "${workspaceRoot}/appflowy_flutter"
|
||||
},
|
||||
{
|
||||
// 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",
|
||||
"name": "AF-desktop: Clean + Rebuild All",
|
||||
"request": "launch",
|
||||
"program": "./lib/main.dart",
|
||||
"type": "dart",
|
||||
@@ -58,14 +41,40 @@
|
||||
"env": {
|
||||
"RUST_LOG": "trace"
|
||||
},
|
||||
"cwd": "${workspaceRoot}/app_flowy"
|
||||
"cwd": "${workspaceRoot}/appflowy_flutter"
|
||||
},
|
||||
{
|
||||
"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}/appflowy_flutter"
|
||||
// },
|
||||
{
|
||||
// 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}/appflowy_flutter"
|
||||
},
|
||||
{
|
||||
// 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",
|
||||
@@ -73,37 +82,32 @@
|
||||
"env": {
|
||||
"RUST_LOG": "info"
|
||||
},
|
||||
"cwd": "${workspaceRoot}/app_flowy"
|
||||
"cwd": "${workspaceRoot}/appflowy_flutter"
|
||||
},
|
||||
{
|
||||
"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
+12
-1
@@ -9,14 +9,25 @@
|
||||
"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,
|
||||
"editor.wordWrapColumn": 80,
|
||||
"editor.minimap.maxColumn": 140,
|
||||
"prettier.printWidth": 140,
|
||||
"editor.wordWrap": "wordWrapColumn",
|
||||
"dart.lineLength": 80,
|
||||
"typescript.validate.enable": true,
|
||||
"javascript.validate.enable": true,
|
||||
"files.associations": {
|
||||
"*.log.*": "log"
|
||||
},
|
||||
|
||||
Vendored
+76
-43
@@ -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": {
|
||||
@@ -117,7 +96,7 @@
|
||||
"type": "shell",
|
||||
"command": "flutter clean",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/app_flowy"
|
||||
"cwd": "${workspaceFolder}/appflowy_flutter"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -125,7 +104,7 @@
|
||||
"type": "shell",
|
||||
"command": "flutter pub get",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/app_flowy"
|
||||
"cwd": "${workspaceFolder}/appflowy_flutter"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -133,7 +112,7 @@
|
||||
"type": "shell",
|
||||
"command": "flutter packages pub get",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/app_flowy"
|
||||
"cwd": "${workspaceFolder}/appflowy_flutter"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -141,7 +120,7 @@
|
||||
"type": "shell",
|
||||
"command": "flutter pub run build_runner build --delete-conflicting-outputs",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/app_flowy"
|
||||
"cwd": "${workspaceFolder}/appflowy_flutter"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -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}"
|
||||
@@ -184,7 +163,61 @@
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": [],
|
||||
"detail": "app_flowy"
|
||||
}
|
||||
"detail": "appflowy_flutter"
|
||||
},
|
||||
{
|
||||
"label": "AF: Tauri UI Dev",
|
||||
"type": "shell",
|
||||
"isBackground": true,
|
||||
"command": "yarn",
|
||||
"args": ["dev"],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/appflowy_tauri"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Tauri UI Build",
|
||||
"type": "shell",
|
||||
"command": "npm run build",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/appflowy_tauri"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Tauri Dev",
|
||||
"type": "shell",
|
||||
"command": "npm run tauri:dev",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/appflowy_tauri"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Tauri Clean",
|
||||
"type": "shell",
|
||||
"command": "cargo make tauri_clean",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Tauri Clean + Dev",
|
||||
"type": "shell",
|
||||
"dependsOrder": "sequence",
|
||||
"dependsOn": [
|
||||
"AF: Tauri Clean",
|
||||
"AF: Tauri UI Dev"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Tauri ESLint",
|
||||
"type": "shell",
|
||||
"command": "npx eslint --fix src",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/appflowy_tauri"
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
+22
-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]
|
||||
@@ -22,12 +23,12 @@ 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.2"
|
||||
FLUTTER_DESKTOP_FEATURES = "dart,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,12 +43,11 @@ 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 = "appflowy_flutter/packages/appflowy_backend"
|
||||
TAURI_BACKEND_SERVICE_PATH = "appflowy_tauri/src/services/backend"
|
||||
# Test default config
|
||||
TEST_CRATE_TYPE = "cdylib"
|
||||
TEST_LIB_EXT = "dylib"
|
||||
TEST_RUST_LOG = "info"
|
||||
TEST_BUILD_FLAG = "debug"
|
||||
TEST_COMPILE_TARGET = "x86_64-apple-darwin"
|
||||
|
||||
@@ -133,6 +133,7 @@ CRATE_TYPE = "cdylib"
|
||||
FLUTTER_OUTPUT_DIR = "Debug"
|
||||
LIB_EXT = "so"
|
||||
LINUX_ARCH = "arm64"
|
||||
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite,openssl_vendored"
|
||||
|
||||
[env.production-linux-aarch64]
|
||||
BUILD_FLAG = "release"
|
||||
@@ -143,6 +144,7 @@ FLUTTER_OUTPUT_DIR = "Release"
|
||||
LIB_EXT = "so"
|
||||
LINUX_ARCH = "arm64"
|
||||
APP_ENVIRONMENT = "production"
|
||||
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite,openssl_vendored"
|
||||
|
||||
[tasks.echo_env]
|
||||
script = ['''
|
||||
@@ -151,7 +153,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 +172,19 @@ BUILD_FLAG = "debug"
|
||||
TARGET_OS = "android"
|
||||
CRATE_TYPE = "cdylib"
|
||||
FLUTTER_OUTPUT_DIR = "Debug"
|
||||
FEATURES = "flutter,openssl_vendored"
|
||||
FLUTTER_DESKTOP_FEATURES = "dart,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 = "dart,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 +196,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 +224,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 +236,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 +252,3 @@ script = ["""
|
||||
cargo build -vv --features=dart
|
||||
"""]
|
||||
script_runner = "@shell"
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
# app_flowy
|
||||
|
||||
A new Flutter project.
|
||||
|
||||
## Getting Started
|
||||
|
||||
This project is a starting point for a Flutter application.
|
||||
|
||||
A few resources to get you started if this is your first Flutter project:
|
||||
|
||||
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
|
||||
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
|
||||
|
||||
For help getting started with Flutter, view our
|
||||
[online documentation](https://flutter.dev/docs), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
||||
|
||||
|
||||
## release check
|
||||
1. [entitlements](https://flutter.dev/desktop#setting-up-entitlements)
|
||||
2. [symbols stripped](https://flutter.dev/docs/development/platform-integration/c-interop)
|
||||
@@ -1,236 +0,0 @@
|
||||
{
|
||||
"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": "브라우저에서 회원가입이 완료되었는지 확인해주세요."
|
||||
},
|
||||
"google": {
|
||||
"title": "GOOGLE SIGN-IN",
|
||||
"instruction1": "구글 연락처를 가져오기 위해서 웹브라우저로 앱을 승인 해야 합니다.",
|
||||
"instruction2": "아이콘을 클릭 또는 텍스트를 선택해서 이 코드를 클립보드로 복사하세요:",
|
||||
"instruction3": "웹브라우저로 다음 링크로 가셔서 위 코드를 입력해주세요:",
|
||||
"instruction4": "가입 완료 후 아래 버튼을 눌러주세요:"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"title": "설정",
|
||||
"menu": {
|
||||
"appearance": "화면",
|
||||
"language": "언어",
|
||||
"user": "사용자",
|
||||
"open": "설정 열기"
|
||||
},
|
||||
"appearance": {
|
||||
"lightLabel": "라이트 모드",
|
||||
"darkLabel": "다크 모드"
|
||||
}
|
||||
},
|
||||
"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": "그리드"
|
||||
},
|
||||
"document": {
|
||||
"menuName": "도큐먼트",
|
||||
"date": {
|
||||
"timeHintTextInTwelveHour": "01:00 PM",
|
||||
"timeHintTextInTwentyFourHour": "13:00"
|
||||
}
|
||||
},
|
||||
"board": {
|
||||
"column": {
|
||||
"create_new_card": "추가"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,233 +0,0 @@
|
||||
{
|
||||
"appName": "AppFlowy",
|
||||
"defaultUsername": "Я",
|
||||
"welcomeText": "Добро пожаловать в @:appName",
|
||||
"githubStarText": "Поставить звезду на GitHub",
|
||||
"subscribeNewsletterText": "Подписаться на рассылку",
|
||||
"letsGoButtonText": "Начнём",
|
||||
"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": "В разработке",
|
||||
"markdown": "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": "Список To-Do",
|
||||
"inlineCode": "Код",
|
||||
"quote": "Цитата",
|
||||
"header": "Заголовок",
|
||||
"highlight": "Выделение"
|
||||
},
|
||||
"tooltip": {
|
||||
"darkMode": "Переключиться в тёмную тему",
|
||||
"openAsPage": "Открыть как страницу",
|
||||
"addNewRow": "Добавить новую строку",
|
||||
"openMenu": "Открыть меню"
|
||||
},
|
||||
"sideBar": {
|
||||
"closeSidebar": "Закрыть боковое меню",
|
||||
"openSidebar": "Открыть боковое меню"
|
||||
},
|
||||
"notifications": {
|
||||
"export": {
|
||||
"markdown": "Заметка экспортирована в Markdown",
|
||||
"path": "Документы/flowy"
|
||||
}
|
||||
},
|
||||
"contactsPage": {
|
||||
"title": "Контакты",
|
||||
"whatsHappening": "Что происходит на этой неделе?",
|
||||
"addContact": "Новый контакт",
|
||||
"editContact": "Редактировать"
|
||||
},
|
||||
"button": {
|
||||
"OK": "OK",
|
||||
"Cancel": "Отмена",
|
||||
"signIn": "Войти",
|
||||
"signOut": "Выйти",
|
||||
"complete": "Завершить",
|
||||
"save": "Сохранить"
|
||||
},
|
||||
"label": {
|
||||
"welcome": "Добро пожаловать!",
|
||||
"firstName": "Имя",
|
||||
"middleName": "Отчество",
|
||||
"lastName": "Фамилия",
|
||||
"stepX": "Этап {X}"
|
||||
},
|
||||
"oAuth": {
|
||||
"err": {
|
||||
"failedTitle": "Ошибка подключения к аккаунту.",
|
||||
"failedMsg": "Убедитесь, что вы завершили вход в своём браузере."
|
||||
},
|
||||
"google": {
|
||||
"title": "Вход через Google",
|
||||
"instruction1": "Чтобы импортировать ваши Google Контакты, вам нужно будет авторизовать приложение через браузер.",
|
||||
"instruction2": "Скопируйте этот код в буфер обмена (нажав кнопку или выделив текст):",
|
||||
"instruction3": "Пройдите по ссылке и введите этот код:",
|
||||
"instruction4": "Нажмите на кнопку ниже, когда завершите вход:"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"title": "Настройки",
|
||||
"menu": {
|
||||
"appearance": "Внешний вид",
|
||||
"language": "Язык",
|
||||
"user": "Пользователь",
|
||||
"open": "Открыть настройки"
|
||||
},
|
||||
"appearance": {
|
||||
"lightLabel": "Светлая",
|
||||
"darkLabel": "Тёмная"
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
"settings": {
|
||||
"filter": "Фильтр",
|
||||
"sortBy": "Сортировать",
|
||||
"Properties": "Свойства",
|
||||
"group": "Группировать"
|
||||
},
|
||||
"field": {
|
||||
"hide": "Скрыть",
|
||||
"insertLeft": "Вставить слева",
|
||||
"insertRight": "Вставить справа",
|
||||
"duplicate": "Дублировать",
|
||||
"delete": "Удалить",
|
||||
"textFieldName": "Текст",
|
||||
"checkboxFieldName": "Чекбокс",
|
||||
"dateFieldName": "Дата",
|
||||
"numberFieldName": "Число",
|
||||
"singleSelectFieldName": "Выбор",
|
||||
"multiSelectFieldName": "Выбор нескольких",
|
||||
"urlFieldName": "URL",
|
||||
"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": "Количество"
|
||||
},
|
||||
"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": "Создать"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:flowy_sdk/protobuf/dart-notify/protobuf.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder/dart_notification.pb.dart';
|
||||
import 'package:flowy_sdk/rust_stream.dart';
|
||||
|
||||
import 'notification_helper.dart';
|
||||
|
||||
// Folder
|
||||
typedef FolderNotificationCallback = void Function(FolderNotification, Either<Uint8List, FlowyError>);
|
||||
|
||||
class FolderNotificationParser extends NotificationParser<FolderNotification, FlowyError> {
|
||||
FolderNotificationParser({String? id, required FolderNotificationCallback callback})
|
||||
: super(
|
||||
id: id,
|
||||
callback: callback,
|
||||
tyParser: (ty) => FolderNotification.valueOf(ty),
|
||||
errorParser: (bytes) => FlowyError.fromBuffer(bytes),
|
||||
);
|
||||
}
|
||||
|
||||
typedef FolderNotificationHandler = Function(FolderNotification ty, Either<Uint8List, FlowyError> result);
|
||||
|
||||
class FolderNotificationListener {
|
||||
StreamSubscription<SubscribeObject>? _subscription;
|
||||
FolderNotificationParser? _parser;
|
||||
|
||||
FolderNotificationListener({required String objectId, required FolderNotificationHandler handler})
|
||||
: _parser = FolderNotificationParser(id: objectId, callback: handler) {
|
||||
_subscription = RustStreamReceiver.listen((observable) => _parser?.parse(observable));
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
_parser = null;
|
||||
await _subscription?.cancel();
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:flowy_sdk/protobuf/dart-notify/protobuf.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
|
||||
import 'package:flowy_sdk/rust_stream.dart';
|
||||
|
||||
import 'notification_helper.dart';
|
||||
|
||||
// GridPB
|
||||
typedef GridNotificationCallback = void Function(
|
||||
GridDartNotification, Either<Uint8List, FlowyError>);
|
||||
|
||||
class GridNotificationParser
|
||||
extends NotificationParser<GridDartNotification, FlowyError> {
|
||||
GridNotificationParser(
|
||||
{String? id, required GridNotificationCallback callback})
|
||||
: super(
|
||||
id: id,
|
||||
callback: callback,
|
||||
tyParser: (ty) => GridDartNotification.valueOf(ty),
|
||||
errorParser: (bytes) => FlowyError.fromBuffer(bytes),
|
||||
);
|
||||
}
|
||||
|
||||
typedef GridNotificationHandler = Function(
|
||||
GridDartNotification ty, Either<Uint8List, FlowyError> result);
|
||||
|
||||
class GridNotificationListener {
|
||||
StreamSubscription<SubscribeObject>? _subscription;
|
||||
GridNotificationParser? _parser;
|
||||
|
||||
GridNotificationListener(
|
||||
{required String objectId, required GridNotificationHandler handler})
|
||||
: _parser = GridNotificationParser(id: objectId, callback: handler) {
|
||||
_subscription =
|
||||
RustStreamReceiver.listen((observable) => _parser?.parse(observable));
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
_parser = null;
|
||||
await _subscription?.cancel();
|
||||
_subscription = null;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:flowy_sdk/protobuf/dart-notify/protobuf.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/rust_stream.dart';
|
||||
|
||||
import 'notification_helper.dart';
|
||||
|
||||
// User
|
||||
typedef UserNotificationCallback = void Function(UserNotification, Either<Uint8List, FlowyError>);
|
||||
|
||||
class UserNotificationParser extends NotificationParser<UserNotification, FlowyError> {
|
||||
UserNotificationParser({required String id, required UserNotificationCallback callback})
|
||||
: super(
|
||||
id: id,
|
||||
callback: callback,
|
||||
tyParser: (ty) => UserNotification.valueOf(ty),
|
||||
errorParser: (bytes) => FlowyError.fromBuffer(bytes),
|
||||
);
|
||||
}
|
||||
|
||||
typedef UserNotificationHandler = Function(UserNotification ty, Either<Uint8List, FlowyError> result);
|
||||
|
||||
class UserNotificationListener {
|
||||
StreamSubscription<SubscribeObject>? _subscription;
|
||||
UserNotificationParser? _parser;
|
||||
|
||||
UserNotificationListener({required String objectId, required UserNotificationHandler handler})
|
||||
: _parser = UserNotificationParser(id: objectId, callback: handler) {
|
||||
_subscription = RustStreamReceiver.listen((observable) => _parser?.parse(observable));
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
_parser = null;
|
||||
await _subscription?.cancel();
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
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';
|
||||
|
||||
class FlowyApp implements EntryPoint {
|
||||
@override
|
||||
Widget create() {
|
||||
return const SplashScreen();
|
||||
}
|
||||
}
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await EasyLocalization.ensureInitialized();
|
||||
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await hotKeyManager.unregisterAll();
|
||||
|
||||
await FlowyRunner.run(FlowyApp());
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
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/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 'dart:async';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
|
||||
|
||||
import 'board_listener.dart';
|
||||
|
||||
typedef OnFieldsChanged = void Function(UnmodifiableListView<FieldInfo>);
|
||||
typedef OnGridChanged = void Function(GridPB);
|
||||
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 OnResetGroups = void Function(List<GroupPB>);
|
||||
|
||||
typedef OnRowsChanged = void Function(
|
||||
List<RowInfo>,
|
||||
RowsChangedReason,
|
||||
);
|
||||
typedef OnError = void Function(FlowyError);
|
||||
|
||||
class BoardDataController {
|
||||
final String gridId;
|
||||
final GridFFIService _gridFFIService;
|
||||
final GridFieldController fieldController;
|
||||
final BoardListener _listener;
|
||||
|
||||
// key: the block id
|
||||
final LinkedHashMap<String, GridBlockCache> _blocks;
|
||||
UnmodifiableMapView<String, GridBlockCache> get blocks =>
|
||||
UnmodifiableMapView(_blocks);
|
||||
|
||||
OnFieldsChanged? _onFieldsChanged;
|
||||
OnGridChanged? _onGridChanged;
|
||||
DidLoadGroups? _didLoadGroup;
|
||||
OnRowsChanged? _onRowsChanged;
|
||||
OnError? _onError;
|
||||
|
||||
List<RowInfo> get rowInfos {
|
||||
final List<RowInfo> rows = [];
|
||||
for (var block in _blocks.values) {
|
||||
rows.addAll(block.rows);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
BoardDataController({required ViewPB view})
|
||||
: gridId = view.id,
|
||||
_listener = BoardListener(view.id),
|
||||
// ignore: prefer_collection_literals
|
||||
_blocks = LinkedHashMap(),
|
||||
_gridFFIService = GridFFIService(gridId: view.id),
|
||||
fieldController = GridFieldController(gridId: view.id);
|
||||
|
||||
void addListener({
|
||||
required OnGridChanged onGridChanged,
|
||||
OnFieldsChanged? onFieldsChanged,
|
||||
required DidLoadGroups didLoadGroups,
|
||||
OnRowsChanged? onRowsChanged,
|
||||
required OnUpdatedGroup onUpdatedGroup,
|
||||
required OnDeletedGroup onDeletedGroup,
|
||||
required OnInsertedGroup onInsertedGroup,
|
||||
required OnResetGroups onResetGroups,
|
||||
required OnError? onError,
|
||||
}) {
|
||||
_onGridChanged = onGridChanged;
|
||||
_onFieldsChanged = onFieldsChanged;
|
||||
_didLoadGroup = didLoadGroups;
|
||||
_onRowsChanged = onRowsChanged;
|
||||
_onError = onError;
|
||||
|
||||
fieldController.addListener(onFields: (fields) {
|
||||
_onFieldsChanged?.call(UnmodifiableListView(fields));
|
||||
});
|
||||
|
||||
_listener.start(
|
||||
onBoardChanged: (result) {
|
||||
result.fold(
|
||||
(changeset) {
|
||||
if (changeset.updateGroups.isNotEmpty) {
|
||||
onUpdatedGroup.call(changeset.updateGroups);
|
||||
}
|
||||
|
||||
if (changeset.deletedGroups.isNotEmpty) {
|
||||
onDeletedGroup.call(changeset.deletedGroups);
|
||||
}
|
||||
|
||||
if (changeset.insertedGroups.isNotEmpty) {
|
||||
onInsertedGroup.call(changeset.insertedGroups);
|
||||
}
|
||||
},
|
||||
(e) => _onError?.call(e),
|
||||
);
|
||||
},
|
||||
onGroupByNewField: (result) {
|
||||
result.fold(
|
||||
(groups) => onResetGroups(groups),
|
||||
(e) => _onError?.call(e),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
);
|
||||
return result.fold(
|
||||
(l) {
|
||||
_loadGroups(grid.blocks);
|
||||
return left(l);
|
||||
},
|
||||
(err) => right(err),
|
||||
);
|
||||
},
|
||||
(err) => right(err),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<RowPB, FlowyError>> createBoardCard(String groupId,
|
||||
{String? startRowId}) {
|
||||
return _gridFFIService.createBoardCard(groupId, startRowId);
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
await _gridFFIService.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();
|
||||
return Future(
|
||||
() => result.fold(
|
||||
(groups) {
|
||||
_didLoadGroup?.call(groups.items);
|
||||
},
|
||||
(err) => _onError?.call(err),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
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:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/group_changeset.pb.dart';
|
||||
|
||||
typedef GroupUpdateValue = Either<GroupViewChangesetPB, FlowyError>;
|
||||
typedef GroupByNewFieldValue = Either<List<GroupPB>, FlowyError>;
|
||||
|
||||
class BoardListener {
|
||||
final String viewId;
|
||||
PublishNotifier<GroupUpdateValue>? _groupUpdateNotifier = PublishNotifier();
|
||||
PublishNotifier<GroupByNewFieldValue>? _groupByNewFieldNotifier =
|
||||
PublishNotifier();
|
||||
GridNotificationListener? _listener;
|
||||
BoardListener(this.viewId);
|
||||
|
||||
void start({
|
||||
required void Function(GroupUpdateValue) onBoardChanged,
|
||||
required void Function(GroupByNewFieldValue) onGroupByNewField,
|
||||
}) {
|
||||
_groupUpdateNotifier?.addPublishListener(onBoardChanged);
|
||||
_groupByNewFieldNotifier?.addPublishListener(onGroupByNewField);
|
||||
_listener = GridNotificationListener(
|
||||
objectId: viewId,
|
||||
handler: _handler,
|
||||
);
|
||||
}
|
||||
|
||||
void _handler(
|
||||
GridDartNotification ty,
|
||||
Either<Uint8List, FlowyError> result,
|
||||
) {
|
||||
switch (ty) {
|
||||
case GridDartNotification.DidUpdateGroupView:
|
||||
result.fold(
|
||||
(payload) => _groupUpdateNotifier?.value =
|
||||
left(GroupViewChangesetPB.fromBuffer(payload)),
|
||||
(error) => _groupUpdateNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
case GridDartNotification.DidGroupByNewField:
|
||||
result.fold(
|
||||
(payload) => _groupByNewFieldNotifier?.value =
|
||||
left(GroupViewChangesetPB.fromBuffer(payload).newGroups),
|
||||
(error) => _groupByNewFieldNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
await _listener?.stop();
|
||||
_groupUpdateNotifier?.dispose();
|
||||
_groupUpdateNotifier = null;
|
||||
|
||||
_groupByNewFieldNotifier?.dispose();
|
||||
_groupByNewFieldNotifier = null;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import 'package:app_flowy/plugins/board/presentation/card/card_cell_builder.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
|
||||
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:flutter/foundation.dart';
|
||||
|
||||
typedef OnCardChanged = void Function(GridCellMap, RowsChangedReason);
|
||||
|
||||
class CardDataController extends BoardCellBuilderDelegate {
|
||||
final RowPB rowPB;
|
||||
final GridFieldController _fieldController;
|
||||
final GridRowCache _rowCache;
|
||||
final List<VoidCallback> _onCardChangedListeners = [];
|
||||
|
||||
CardDataController({
|
||||
required this.rowPB,
|
||||
required GridFieldController fieldController,
|
||||
required GridRowCache rowCache,
|
||||
}) : _fieldController = fieldController,
|
||||
_rowCache = rowCache;
|
||||
|
||||
GridCellMap loadData() {
|
||||
return _rowCache.loadGridCells(rowPB.id);
|
||||
}
|
||||
|
||||
void addListener({OnCardChanged? onRowChanged}) {
|
||||
_onCardChangedListeners.add(_rowCache.addListener(
|
||||
rowId: rowPB.id,
|
||||
onCellUpdated: onRowChanged,
|
||||
));
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
for (final fn in _onCardChangedListeners) {
|
||||
_rowCache.removeRowListener(fn);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
GridCellFieldNotifier buildFieldNotifier() {
|
||||
return GridCellFieldNotifier(
|
||||
notifier: GridCellFieldNotifierImpl(_fieldController));
|
||||
}
|
||||
|
||||
@override
|
||||
GridCellCache get cellCache => _rowCache.cellCache;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||
|
||||
class BoardGroupService {
|
||||
final String gridId;
|
||||
FieldPB? groupField;
|
||||
|
||||
BoardGroupService(this.gridId);
|
||||
|
||||
void setGroupField(FieldPB field) {
|
||||
groupField = field;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class CanBeGroupField {
|
||||
String get groupContent;
|
||||
}
|
||||
|
||||
// class SingleSelectGroup extends CanBeGroupField {
|
||||
// final SingleSelectTypeOptionContext typeOptionContext;
|
||||
// }
|
||||
@@ -1,51 +0,0 @@
|
||||
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:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/group_changeset.pb.dart';
|
||||
|
||||
typedef UpdateGroupNotifiedValue = Either<GroupRowsNotificationPB, FlowyError>;
|
||||
|
||||
class GroupListener {
|
||||
final GroupPB group;
|
||||
PublishNotifier<UpdateGroupNotifiedValue>? _groupNotifier = PublishNotifier();
|
||||
GridNotificationListener? _listener;
|
||||
GroupListener(this.group);
|
||||
|
||||
void start({
|
||||
required void Function(UpdateGroupNotifiedValue) onGroupChanged,
|
||||
}) {
|
||||
_groupNotifier?.addPublishListener(onGroupChanged);
|
||||
_listener = GridNotificationListener(
|
||||
objectId: group.groupId,
|
||||
handler: _handler,
|
||||
);
|
||||
}
|
||||
|
||||
void _handler(
|
||||
GridDartNotification ty,
|
||||
Either<Uint8List, FlowyError> result,
|
||||
) {
|
||||
switch (ty) {
|
||||
case GridDartNotification.DidUpdateGroup:
|
||||
result.fold(
|
||||
(payload) => _groupNotifier?.value =
|
||||
left(GroupRowsNotificationPB.fromBuffer(payload)),
|
||||
(error) => _groupNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
await _listener?.stop();
|
||||
_groupNotifier?.dispose();
|
||||
_groupNotifier = null;
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
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:flutter/material.dart';
|
||||
|
||||
import 'board_cell.dart';
|
||||
import 'board_checkbox_cell.dart';
|
||||
import 'board_date_cell.dart';
|
||||
import 'board_number_cell.dart';
|
||||
import 'board_select_option_cell.dart';
|
||||
import 'board_text_cell.dart';
|
||||
import 'board_url_cell.dart';
|
||||
|
||||
abstract class BoardCellBuilderDelegate
|
||||
extends GridCellControllerBuilderDelegate {
|
||||
GridCellCache get cellCache;
|
||||
}
|
||||
|
||||
class BoardCellBuilder {
|
||||
final BoardCellBuilderDelegate delegate;
|
||||
|
||||
BoardCellBuilder(this.delegate);
|
||||
|
||||
Widget buildCell(
|
||||
String groupId,
|
||||
GridCellIdentifier cellId,
|
||||
EditableCellNotifier cellNotifier,
|
||||
) {
|
||||
final cellControllerBuilder = GridCellControllerBuilder(
|
||||
delegate: delegate,
|
||||
cellId: cellId,
|
||||
cellCache: delegate.cellCache,
|
||||
);
|
||||
|
||||
final key = cellId.key();
|
||||
switch (cellId.fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
return BoardCheckboxCell(
|
||||
groupId: groupId,
|
||||
cellControllerBuilder: cellControllerBuilder,
|
||||
key: key,
|
||||
);
|
||||
case FieldType.DateTime:
|
||||
return BoardDateCell(
|
||||
groupId: groupId,
|
||||
cellControllerBuilder: cellControllerBuilder,
|
||||
key: key,
|
||||
);
|
||||
case FieldType.SingleSelect:
|
||||
return BoardSelectOptionCell(
|
||||
groupId: groupId,
|
||||
cellControllerBuilder: cellControllerBuilder,
|
||||
key: key,
|
||||
);
|
||||
case FieldType.MultiSelect:
|
||||
return BoardSelectOptionCell(
|
||||
groupId: groupId,
|
||||
cellControllerBuilder: cellControllerBuilder,
|
||||
editableNotifier: cellNotifier,
|
||||
key: key,
|
||||
);
|
||||
case FieldType.Number:
|
||||
return BoardNumberCell(
|
||||
groupId: groupId,
|
||||
cellControllerBuilder: cellControllerBuilder,
|
||||
key: key,
|
||||
);
|
||||
case FieldType.RichText:
|
||||
return BoardTextCell(
|
||||
groupId: groupId,
|
||||
cellControllerBuilder: cellControllerBuilder,
|
||||
editableNotifier: cellNotifier,
|
||||
key: key,
|
||||
);
|
||||
case FieldType.URL:
|
||||
return BoardUrlCell(
|
||||
groupId: groupId,
|
||||
cellControllerBuilder: cellControllerBuilder,
|
||||
key: key,
|
||||
);
|
||||
}
|
||||
throw UnimplementedError;
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
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:appflowy_editor/appflowy_editor.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 'application/doc_bloc.dart';
|
||||
|
||||
class DocumentPage extends StatefulWidget {
|
||||
final VoidCallback onDeleted;
|
||||
final ViewPB view;
|
||||
|
||||
DocumentPage({
|
||||
required this.view,
|
||||
required this.onDeleted,
|
||||
Key? key,
|
||||
}) : super(key: ValueKey(view.id));
|
||||
|
||||
@override
|
||||
State<DocumentPage> createState() => _DocumentPageState();
|
||||
}
|
||||
|
||||
class _DocumentPageState extends State<DocumentPage> {
|
||||
late DocumentBloc documentBloc;
|
||||
final FocusNode _focusNode = FocusNode();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
// The appflowy editor use Intl as localization, set the default language as fallback.
|
||||
Intl.defaultLocale = 'en_US';
|
||||
documentBloc = getIt<DocumentBloc>(param1: super.widget.view)
|
||||
..add(const DocumentEvent.initial());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider<DocumentBloc>.value(value: documentBloc),
|
||||
],
|
||||
child:
|
||||
BlocBuilder<DocumentBloc, DocumentState>(builder: (context, state) {
|
||||
return state.loadingState.map(
|
||||
loading: (_) => SizedBox.expand(
|
||||
child: Container(color: Colors.transparent),
|
||||
),
|
||||
finish: (result) => result.successOrFail.fold(
|
||||
(_) {
|
||||
if (state.forceClose) {
|
||||
widget.onDeleted();
|
||||
return const SizedBox();
|
||||
} else {
|
||||
return _renderDocument(context, state);
|
||||
}
|
||||
},
|
||||
(err) => FlowyErrorPage(err.toString()),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
documentBloc.close();
|
||||
_focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Widget _renderDocument(BuildContext context, DocumentState state) {
|
||||
return Column(
|
||||
children: [
|
||||
if (state.isDeleted) _renderBanner(context),
|
||||
// AppFlowy Editor
|
||||
_renderAppFlowyEditor(
|
||||
context.read<DocumentBloc>().editorState,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _renderBanner(BuildContext context) {
|
||||
return DocumentBanner(
|
||||
onRestore: () =>
|
||||
context.read<DocumentBloc>().add(const DocumentEvent.restorePage()),
|
||||
onDelete: () => context
|
||||
.read<DocumentBloc>()
|
||||
.add(const DocumentEvent.deletePermanently()),
|
||||
);
|
||||
}
|
||||
|
||||
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(),
|
||||
},
|
||||
shortcutEvents: [
|
||||
insertHorizontalRule,
|
||||
],
|
||||
themeData: theme.copyWith(extensions: [
|
||||
...theme.extensions.values,
|
||||
customEditorTheme(context),
|
||||
...customPluginTheme(context),
|
||||
]),
|
||||
);
|
||||
return Expanded(
|
||||
child: Center(
|
||||
child: Container(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: editorMaxWidth,
|
||||
),
|
||||
child: editor,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
-342
@@ -1,342 +0,0 @@
|
||||
part of 'cell_service.dart';
|
||||
|
||||
typedef GridCellController = IGridCellController<String, String>;
|
||||
typedef GridCheckboxCellController = IGridCellController<String, String>;
|
||||
typedef GridNumberCellController = IGridCellController<String, String>;
|
||||
typedef GridSelectOptionCellController
|
||||
= IGridCellController<SelectOptionCellDataPB, String>;
|
||||
typedef GridDateCellController
|
||||
= IGridCellController<DateCellDataPB, CalendarData>;
|
||||
typedef GridURLCellController = IGridCellController<URLCellDataPB, String>;
|
||||
|
||||
abstract class GridCellControllerBuilderDelegate {
|
||||
GridCellFieldNotifier buildFieldNotifier();
|
||||
}
|
||||
|
||||
class GridCellControllerBuilder {
|
||||
final GridCellIdentifier _cellId;
|
||||
final GridCellCache _cellCache;
|
||||
final GridCellControllerBuilderDelegate delegate;
|
||||
|
||||
GridCellControllerBuilder({
|
||||
required this.delegate,
|
||||
required GridCellIdentifier cellId,
|
||||
required GridCellCache cellCache,
|
||||
}) : _cellCache = cellCache,
|
||||
_cellId = cellId;
|
||||
|
||||
IGridCellController build() {
|
||||
final cellFieldNotifier = delegate.buildFieldNotifier();
|
||||
switch (_cellId.fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
cellId: _cellId,
|
||||
parser: StringCellDataParser(),
|
||||
);
|
||||
return GridCellController(
|
||||
cellId: _cellId,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
fieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(cellId: _cellId),
|
||||
);
|
||||
case FieldType.DateTime:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
cellId: _cellId,
|
||||
parser: DateCellDataParser(),
|
||||
reloadOnFieldChanged: true,
|
||||
);
|
||||
|
||||
return GridDateCellController(
|
||||
cellId: _cellId,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
fieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: DateCellDataPersistence(cellId: _cellId),
|
||||
);
|
||||
case FieldType.Number:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
cellId: _cellId,
|
||||
parser: StringCellDataParser(),
|
||||
reloadOnFieldChanged: true,
|
||||
);
|
||||
return GridNumberCellController(
|
||||
cellId: _cellId,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
fieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(cellId: _cellId),
|
||||
);
|
||||
case FieldType.RichText:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
cellId: _cellId,
|
||||
parser: StringCellDataParser(),
|
||||
);
|
||||
return GridCellController(
|
||||
cellId: _cellId,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
fieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(cellId: _cellId),
|
||||
);
|
||||
case FieldType.MultiSelect:
|
||||
case FieldType.SingleSelect:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
cellId: _cellId,
|
||||
parser: SelectOptionCellDataParser(),
|
||||
reloadOnFieldChanged: true,
|
||||
);
|
||||
|
||||
return GridSelectOptionCellController(
|
||||
cellId: _cellId,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
fieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(cellId: _cellId),
|
||||
);
|
||||
|
||||
case FieldType.URL:
|
||||
final cellDataLoader = GridCellDataLoader(
|
||||
cellId: _cellId,
|
||||
parser: URLCellDataParser(),
|
||||
);
|
||||
return GridURLCellController(
|
||||
cellId: _cellId,
|
||||
cellCache: _cellCache,
|
||||
cellDataLoader: cellDataLoader,
|
||||
fieldNotifier: cellFieldNotifier,
|
||||
cellDataPersistence: CellDataPersistence(cellId: _cellId),
|
||||
);
|
||||
}
|
||||
throw UnimplementedError;
|
||||
}
|
||||
}
|
||||
|
||||
/// IGridCellController is used to manipulate the cell and receive notifications.
|
||||
/// * Read/Write cell data
|
||||
/// * Listen on field/cell notifications.
|
||||
///
|
||||
/// Generic T represents the type of the cell data.
|
||||
/// Generic D represents the type of data that will be saved to the disk
|
||||
///
|
||||
// ignore: must_be_immutable
|
||||
class IGridCellController<T, D> extends Equatable {
|
||||
final GridCellIdentifier cellId;
|
||||
final GridCellCache _cellsCache;
|
||||
final GridCellCacheKey _cacheKey;
|
||||
final FieldService _fieldService;
|
||||
final GridCellFieldNotifier _fieldNotifier;
|
||||
final GridCellDataLoader<T> _cellDataLoader;
|
||||
final IGridCellDataPersistence<D> _cellDataPersistence;
|
||||
|
||||
CellListener? _cellListener;
|
||||
ValueNotifier<T?>? _cellDataNotifier;
|
||||
|
||||
bool isListening = false;
|
||||
VoidCallback? _onFieldChangedFn;
|
||||
Timer? _loadDataOperation;
|
||||
Timer? _saveDataOperation;
|
||||
bool _isDispose = false;
|
||||
|
||||
IGridCellController({
|
||||
required this.cellId,
|
||||
required GridCellCache cellCache,
|
||||
required GridCellFieldNotifier fieldNotifier,
|
||||
required GridCellDataLoader<T> cellDataLoader,
|
||||
required IGridCellDataPersistence<D> cellDataPersistence,
|
||||
}) : _cellsCache = cellCache,
|
||||
_cellDataLoader = cellDataLoader,
|
||||
_cellDataPersistence = cellDataPersistence,
|
||||
_fieldNotifier = fieldNotifier,
|
||||
_fieldService = FieldService(
|
||||
gridId: cellId.gridId,
|
||||
fieldId: cellId.fieldInfo.id,
|
||||
),
|
||||
_cacheKey = GridCellCacheKey(
|
||||
rowId: cellId.rowId,
|
||||
fieldId: cellId.fieldInfo.id,
|
||||
);
|
||||
|
||||
String get gridId => cellId.gridId;
|
||||
|
||||
String get rowId => cellId.rowId;
|
||||
|
||||
String get fieldId => cellId.fieldInfo.id;
|
||||
|
||||
FieldInfo get fieldInfo => cellId.fieldInfo;
|
||||
|
||||
FieldType get fieldType => cellId.fieldInfo.fieldType;
|
||||
|
||||
VoidCallback? startListening({
|
||||
required void Function(T?) onCellChanged,
|
||||
VoidCallback? onCellFieldChanged,
|
||||
}) {
|
||||
if (isListening) {
|
||||
Log.error("Already started. It seems like you should call clone first");
|
||||
return null;
|
||||
}
|
||||
isListening = true;
|
||||
|
||||
_cellDataNotifier = ValueNotifier(_cellsCache.get(_cacheKey));
|
||||
_cellListener =
|
||||
CellListener(rowId: cellId.rowId, fieldId: cellId.fieldInfo.id);
|
||||
|
||||
/// 1.Listen on user edit event and load the new cell data if needed.
|
||||
/// For example:
|
||||
/// user input: 12
|
||||
/// cell display: $12
|
||||
_cellListener?.start(onCellChanged: (result) {
|
||||
result.fold(
|
||||
(_) {
|
||||
_cellsCache.remove(_cacheKey);
|
||||
_loadData();
|
||||
},
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
|
||||
/// 2.Listen on the field event and load the cell data if needed.
|
||||
_onFieldChangedFn = () {
|
||||
if (onCellFieldChanged != null) {
|
||||
onCellFieldChanged();
|
||||
}
|
||||
|
||||
/// reloadOnFieldChanged should be true if you need to load the data when the corresponding field is changed
|
||||
/// For example:
|
||||
/// ¥12 -> $12
|
||||
if (_cellDataLoader.reloadOnFieldChanged) {
|
||||
_loadData();
|
||||
}
|
||||
};
|
||||
|
||||
_fieldNotifier.register(_cacheKey, _onFieldChangedFn!);
|
||||
|
||||
/// Notify the listener, the cell data was changed.
|
||||
onCellChangedFn() => onCellChanged(_cellDataNotifier?.value);
|
||||
_cellDataNotifier?.addListener(onCellChangedFn);
|
||||
|
||||
// Return the function pointer that can be used when calling removeListener.
|
||||
return onCellChangedFn;
|
||||
}
|
||||
|
||||
void removeListener(VoidCallback fn) {
|
||||
_cellDataNotifier?.removeListener(fn);
|
||||
}
|
||||
|
||||
/// Return the cell data.
|
||||
/// The cell data will be read from the Cache first, and load from disk if it does not exist.
|
||||
/// You can set [loadIfNotExist] to false (default is true) to disable loading the cell data.
|
||||
T? getCellData({bool loadIfNotExist = true}) {
|
||||
final data = _cellsCache.get(_cacheKey);
|
||||
if (data == null && loadIfNotExist) {
|
||||
_loadData();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/// Return the TypeOptionPB that can be parsed into corresponding class using the [parser].
|
||||
/// [PD] is the type that the parser return.
|
||||
Future<Either<PD, FlowyError>>
|
||||
getFieldTypeOption<PD, P extends TypeOptionDataParser>(P parser) {
|
||||
return _fieldService
|
||||
.getFieldTypeOptionData(fieldType: fieldType)
|
||||
.then((result) {
|
||||
return result.fold(
|
||||
(data) => left(parser.fromBuffer(data.typeOptionData)),
|
||||
(err) => right(err),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Save the cell data to disk
|
||||
/// You can set [deduplicate] to true (default is false) to reduce the save operation.
|
||||
/// It's useful when you call this method when user editing the [TextField].
|
||||
/// The default debounce interval is 300 milliseconds.
|
||||
void saveCellData(D data,
|
||||
{bool deduplicate = false,
|
||||
void Function(Option<FlowyError>)? resultCallback}) async {
|
||||
if (deduplicate) {
|
||||
_loadDataOperation?.cancel();
|
||||
|
||||
_saveDataOperation?.cancel();
|
||||
_saveDataOperation = Timer(const Duration(milliseconds: 300), () async {
|
||||
final result = await _cellDataPersistence.save(data);
|
||||
if (resultCallback != null) {
|
||||
resultCallback(result);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
final result = await _cellDataPersistence.save(data);
|
||||
if (resultCallback != null) {
|
||||
resultCallback(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _loadData() {
|
||||
_saveDataOperation?.cancel();
|
||||
|
||||
_loadDataOperation?.cancel();
|
||||
_loadDataOperation = Timer(const Duration(milliseconds: 10), () {
|
||||
_cellDataLoader.loadData().then((data) {
|
||||
if (data != null) {
|
||||
_cellsCache.insert(_cacheKey, GridCell(object: data));
|
||||
} else {
|
||||
_cellsCache.remove(_cacheKey);
|
||||
}
|
||||
|
||||
_cellDataNotifier?.value = data;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
if (_isDispose) {
|
||||
Log.error("$this should only dispose once");
|
||||
return;
|
||||
}
|
||||
_isDispose = true;
|
||||
await _cellListener?.stop();
|
||||
_loadDataOperation?.cancel();
|
||||
_saveDataOperation?.cancel();
|
||||
_cellDataNotifier = null;
|
||||
|
||||
if (_onFieldChangedFn != null) {
|
||||
_fieldNotifier.unregister(_cacheKey, _onFieldChangedFn!);
|
||||
await _fieldNotifier.dispose();
|
||||
_onFieldChangedFn = null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object> get props =>
|
||||
[_cellsCache.get(_cacheKey) ?? "", cellId.rowId + cellId.fieldInfo.id];
|
||||
}
|
||||
|
||||
class GridCellFieldNotifierImpl extends IGridCellFieldNotifier {
|
||||
final GridFieldController _fieldController;
|
||||
OnReceiveUpdateFields? _onChangesetFn;
|
||||
|
||||
GridCellFieldNotifierImpl(GridFieldController cache)
|
||||
: _fieldController = cache;
|
||||
|
||||
@override
|
||||
void onCellDispose() {
|
||||
if (_onChangesetFn != null) {
|
||||
_fieldController.removeListener(onChangesetListener: _onChangesetFn!);
|
||||
_onChangesetFn = null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onCellFieldChanged(void Function(FieldInfo) callback) {
|
||||
_onChangesetFn = (List<FieldInfo> filedInfos) {
|
||||
for (final field in filedInfos) {
|
||||
callback(field);
|
||||
}
|
||||
};
|
||||
_fieldController.addListener(
|
||||
onFieldsUpdated: _onChangesetFn,
|
||||
);
|
||||
}
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'cell_service.dart';
|
||||
|
||||
abstract class IGridCellFieldNotifier {
|
||||
void onCellFieldChanged(void Function(FieldInfo) callback);
|
||||
void onCellDispose();
|
||||
}
|
||||
|
||||
/// GridPB's cell helper wrapper that enables each cell will get notified when the corresponding field was changed.
|
||||
/// You Register an onFieldChanged callback to listen to the cell changes, and unregister if you don't want to listen.
|
||||
class GridCellFieldNotifier {
|
||||
final IGridCellFieldNotifier notifier;
|
||||
|
||||
/// fieldId: {objectId: callback}
|
||||
final Map<String, Map<String, List<VoidCallback>>> _fieldListenerByFieldId =
|
||||
{};
|
||||
|
||||
GridCellFieldNotifier({required this.notifier}) {
|
||||
notifier.onCellFieldChanged(
|
||||
(field) {
|
||||
final map = _fieldListenerByFieldId[field.id];
|
||||
if (map != null) {
|
||||
for (final callbacks in map.values) {
|
||||
for (final callback in callbacks) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
void register(GridCellCacheKey cacheKey, VoidCallback onFieldChanged) {
|
||||
var map = _fieldListenerByFieldId[cacheKey.fieldId];
|
||||
if (map == null) {
|
||||
_fieldListenerByFieldId[cacheKey.fieldId] = {};
|
||||
map = _fieldListenerByFieldId[cacheKey.fieldId];
|
||||
map![cacheKey.rowId] = [onFieldChanged];
|
||||
} else {
|
||||
var objects = map[cacheKey.rowId];
|
||||
if (objects == null) {
|
||||
map[cacheKey.rowId] = [onFieldChanged];
|
||||
} else {
|
||||
objects.add(onFieldChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void unregister(GridCellCacheKey cacheKey, VoidCallback fn) {
|
||||
var callbacks = _fieldListenerByFieldId[cacheKey.fieldId]?[cacheKey.rowId];
|
||||
final index = callbacks?.indexWhere((callback) => callback == fn);
|
||||
if (index != null && index != -1) {
|
||||
callbacks?.removeAt(index);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
notifier.onCellDispose();
|
||||
_fieldListenerByFieldId.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option_entities.pb.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/cell/cell_listener.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
|
||||
import 'dart:convert' show utf8;
|
||||
|
||||
import '../../field/field_controller.dart';
|
||||
import '../../field/type_option/type_option_context.dart';
|
||||
import 'cell_field_notifier.dart';
|
||||
part 'cell_service.freezed.dart';
|
||||
part 'cell_data_loader.dart';
|
||||
part 'cell_controller.dart';
|
||||
part 'cell_cache.dart';
|
||||
part 'cell_data_persistence.dart';
|
||||
|
||||
// key: rowId
|
||||
|
||||
class CellService {
|
||||
CellService();
|
||||
|
||||
Future<Either<void, FlowyError>> updateCell({
|
||||
required GridCellIdentifier cellId,
|
||||
required String data,
|
||||
}) {
|
||||
final payload = CellChangesetPB.create()
|
||||
..gridId = cellId.gridId
|
||||
..fieldId = cellId.fieldId
|
||||
..rowId = cellId.rowId
|
||||
..content = data;
|
||||
return GridEventUpdateCell(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<CellPB, FlowyError>> getCell({
|
||||
required GridCellIdentifier cellId,
|
||||
}) {
|
||||
final payload = CellPathPB.create()
|
||||
..gridId = cellId.gridId
|
||||
..fieldId = cellId.fieldId
|
||||
..rowId = cellId.rowId;
|
||||
return GridEventGetCell(payload).send();
|
||||
}
|
||||
}
|
||||
|
||||
/// Id of the cell
|
||||
/// We can locate the cell by using gridId + rowId + field.id.
|
||||
@freezed
|
||||
class GridCellIdentifier with _$GridCellIdentifier {
|
||||
const factory GridCellIdentifier({
|
||||
required String gridId,
|
||||
required String rowId,
|
||||
required FieldInfo fieldInfo,
|
||||
}) = _GridCellIdentifier;
|
||||
|
||||
// ignore: unused_element
|
||||
const GridCellIdentifier._();
|
||||
|
||||
String get fieldId => fieldInfo.id;
|
||||
|
||||
FieldType get fieldType => fieldInfo.fieldType;
|
||||
|
||||
ValueKey key() {
|
||||
return ValueKey("$rowId$fieldId${fieldInfo.fieldType}");
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_service.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart';
|
||||
import 'cell_service/cell_service.dart';
|
||||
|
||||
class SelectOptionService {
|
||||
final GridCellIdentifier cellId;
|
||||
SelectOptionService({required this.cellId});
|
||||
|
||||
String get gridId => cellId.gridId;
|
||||
String get fieldId => cellId.fieldInfo.id;
|
||||
String get rowId => cellId.rowId;
|
||||
|
||||
Future<Either<Unit, FlowyError>> create({required String name}) {
|
||||
return TypeOptionFFIService(gridId: gridId, fieldId: fieldId)
|
||||
.newOption(name: name)
|
||||
.then(
|
||||
(result) {
|
||||
return result.fold(
|
||||
(option) {
|
||||
final cellIdentifier = CellPathPB.create()
|
||||
..gridId = gridId
|
||||
..fieldId = fieldId
|
||||
..rowId = rowId;
|
||||
final payload = SelectOptionChangesetPB.create()
|
||||
..insertOptions.add(option)
|
||||
..cellIdentifier = cellIdentifier;
|
||||
return GridEventUpdateSelectOption(payload).send();
|
||||
},
|
||||
(r) => right(r),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> update({
|
||||
required SelectOptionPB option,
|
||||
}) {
|
||||
final payload = SelectOptionChangesetPB.create()
|
||||
..updateOptions.add(option)
|
||||
..cellIdentifier = _cellIdentifier();
|
||||
return GridEventUpdateSelectOption(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> delete(
|
||||
{required Iterable<SelectOptionPB> options}) {
|
||||
final payload = SelectOptionChangesetPB.create()
|
||||
..deleteOptions.addAll(options)
|
||||
..cellIdentifier = _cellIdentifier();
|
||||
|
||||
return GridEventUpdateSelectOption(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<SelectOptionCellDataPB, FlowyError>> getOptionContext() {
|
||||
final payload = CellPathPB.create()
|
||||
..gridId = gridId
|
||||
..fieldId = fieldId
|
||||
..rowId = rowId;
|
||||
|
||||
return GridEventGetSelectOptionCellData(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<void, FlowyError>> select(
|
||||
{required Iterable<String> optionIds}) {
|
||||
final payload = SelectOptionCellChangesetPB.create()
|
||||
..cellIdentifier = _cellIdentifier()
|
||||
..insertOptionIds.addAll(optionIds);
|
||||
return GridEventUpdateSelectOptionCell(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<void, FlowyError>> unSelect(
|
||||
{required Iterable<String> optionIds}) {
|
||||
final payload = SelectOptionCellChangesetPB.create()
|
||||
..cellIdentifier = _cellIdentifier()
|
||||
..deleteOptionIds.addAll(optionIds);
|
||||
return GridEventUpdateSelectOptionCell(payload).send();
|
||||
}
|
||||
|
||||
CellPathPB _cellIdentifier() {
|
||||
return CellPathPB.create()
|
||||
..gridId = gridId
|
||||
..fieldId = fieldId
|
||||
..rowId = rowId;
|
||||
}
|
||||
}
|
||||
@@ -1,542 +0,0 @@
|
||||
import 'dart:collection';
|
||||
import 'package:app_flowy/plugins/grid/application/field/grid_listener.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/filter/filter_listener.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/filter/filter_service.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/grid_service.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/setting/setting_listener.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/setting/setting_service.dart';
|
||||
import 'package:app_flowy/plugins/grid/presentation/widgets/filter/filter_info.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/setting_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/util.pb.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import '../row/row_cache.dart';
|
||||
|
||||
class _GridFieldNotifier extends ChangeNotifier {
|
||||
List<FieldInfo> _fieldInfos = [];
|
||||
|
||||
set fieldInfos(List<FieldInfo> fieldInfos) {
|
||||
_fieldInfos = fieldInfos;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void notify() {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<FieldInfo> get fieldInfos => _fieldInfos;
|
||||
}
|
||||
|
||||
class _GridFilterNotifier extends ChangeNotifier {
|
||||
List<FilterInfo> _filters = [];
|
||||
|
||||
set filters(List<FilterInfo> filters) {
|
||||
_filters = filters;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void notify() {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<FilterInfo> get filters => _filters;
|
||||
}
|
||||
|
||||
typedef OnReceiveUpdateFields = void Function(List<FieldInfo>);
|
||||
typedef OnReceiveFields = void Function(List<FieldInfo>);
|
||||
typedef OnReceiveFilters = void Function(List<FilterInfo>);
|
||||
|
||||
class GridFieldController {
|
||||
final String gridId;
|
||||
// Listeners
|
||||
final GridFieldsListener _fieldListener;
|
||||
final SettingListener _settingListener;
|
||||
final FiltersListener _filterListener;
|
||||
|
||||
// FFI services
|
||||
final GridFFIService _gridFFIService;
|
||||
final SettingFFIService _settingFFIService;
|
||||
final FilterFFIService _filterFFIService;
|
||||
|
||||
// Field callbacks
|
||||
final Map<OnReceiveFields, VoidCallback> _fieldCallbacks = {};
|
||||
_GridFieldNotifier? _fieldNotifier = _GridFieldNotifier();
|
||||
|
||||
// Field updated callbacks
|
||||
final Map<OnReceiveUpdateFields, void Function(List<FieldInfo>)>
|
||||
_updatedFieldCallbacks = {};
|
||||
|
||||
// Group callbacks
|
||||
final Map<String, GroupConfigurationPB> _groupConfigurationByFieldId = {};
|
||||
|
||||
// Filter callbacks
|
||||
final Map<OnReceiveFilters, VoidCallback> _filterCallbacks = {};
|
||||
_GridFilterNotifier? _filterNotifier = _GridFilterNotifier();
|
||||
final Map<String, FilterPB> _filterPBByFieldId = {};
|
||||
|
||||
// Getters
|
||||
List<FieldInfo> get fieldInfos => [..._fieldNotifier?.fieldInfos ?? []];
|
||||
List<FilterInfo> get filterInfos => [..._filterNotifier?.filters ?? []];
|
||||
FieldInfo? getField(String fieldId) {
|
||||
final fields = _fieldNotifier?.fieldInfos
|
||||
.where((element) => element.id == fieldId)
|
||||
.toList() ??
|
||||
[];
|
||||
if (fields.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
assert(fields.length == 1);
|
||||
return fields.first;
|
||||
}
|
||||
|
||||
FilterInfo? getFilter(String filterId) {
|
||||
final filters = _filterNotifier?.filters
|
||||
.where((element) => element.filter.id == filterId)
|
||||
.toList() ??
|
||||
[];
|
||||
if (filters.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
assert(filters.length == 1);
|
||||
return filters.first;
|
||||
}
|
||||
|
||||
GridFieldController({required this.gridId})
|
||||
: _fieldListener = GridFieldsListener(gridId: gridId),
|
||||
_settingListener = SettingListener(gridId: gridId),
|
||||
_filterListener = FiltersListener(viewId: gridId),
|
||||
_gridFFIService = GridFFIService(gridId: gridId),
|
||||
_filterFFIService = FilterFFIService(viewId: gridId),
|
||||
_settingFFIService = SettingFFIService(viewId: gridId) {
|
||||
//Listen on field's changes
|
||||
_listenOnFieldChanges();
|
||||
|
||||
//Listen on setting changes
|
||||
_listenOnSettingChanges();
|
||||
|
||||
//Listen on the fitler changes
|
||||
_listenOnFilterChanges();
|
||||
|
||||
_settingFFIService.getSetting().then((result) {
|
||||
result.fold(
|
||||
(setting) => _updateSettingConfiguration(setting),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void _listenOnFilterChanges() {
|
||||
//Listen on the fitler changes
|
||||
_filterListener.start(onFilterChanged: (result) {
|
||||
result.fold(
|
||||
(changeset) {
|
||||
final List<FilterInfo> filters = filterInfos;
|
||||
// Deletes the filters
|
||||
final deleteFilterIds =
|
||||
changeset.deleteFilters.map((e) => e.id).toList();
|
||||
if (deleteFilterIds.isNotEmpty) {
|
||||
filters.retainWhere(
|
||||
(element) => !deleteFilterIds.contains(element.filter.id),
|
||||
);
|
||||
|
||||
_filterPBByFieldId.removeWhere(
|
||||
(key, value) => deleteFilterIds.contains(value.id));
|
||||
}
|
||||
|
||||
// Inserts the new filter if it's not exist
|
||||
for (final newFilter in changeset.insertFilters) {
|
||||
final filterIndex = filters
|
||||
.indexWhere((element) => element.filter.id == newFilter.id);
|
||||
if (filterIndex == -1) {
|
||||
final fieldInfo = _findFieldInfoForFilter(fieldInfos, newFilter);
|
||||
if (fieldInfo != null) {
|
||||
_filterPBByFieldId[fieldInfo.id] = newFilter;
|
||||
filters.add(FilterInfo(gridId, newFilter, fieldInfo));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (final updatedFilter in changeset.updateFilters) {
|
||||
final filterIndex = filters.indexWhere(
|
||||
(element) => element.filter.id == updatedFilter.filterId,
|
||||
);
|
||||
// Remove the old filter
|
||||
if (filterIndex != -1) {
|
||||
filters.removeAt(filterIndex);
|
||||
_filterPBByFieldId.removeWhere(
|
||||
(key, value) => value.id == updatedFilter.filterId);
|
||||
}
|
||||
|
||||
// Insert the filter if there is a fitler and its field info is
|
||||
// not null
|
||||
if (updatedFilter.hasFilter()) {
|
||||
final fieldInfo = _findFieldInfoForFilter(
|
||||
fieldInfos,
|
||||
updatedFilter.filter,
|
||||
);
|
||||
|
||||
if (fieldInfo != null) {
|
||||
// Insert the filter with the position: filterIndex, otherwise,
|
||||
// append it to the end of the list.
|
||||
final filterInfo =
|
||||
FilterInfo(gridId, updatedFilter.filter, fieldInfo);
|
||||
if (filterIndex != -1) {
|
||||
filters.insert(filterIndex, filterInfo);
|
||||
} else {
|
||||
filters.add(filterInfo);
|
||||
}
|
||||
_filterPBByFieldId[fieldInfo.id] = updatedFilter.filter;
|
||||
}
|
||||
}
|
||||
}
|
||||
_updateFieldInfos();
|
||||
_filterNotifier?.filters = filters;
|
||||
},
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void _listenOnSettingChanges() {
|
||||
//Listen on setting changes
|
||||
_settingListener.start(onSettingUpdated: (result) {
|
||||
result.fold(
|
||||
(setting) => _updateSettingConfiguration(setting),
|
||||
(r) => Log.error(r),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void _listenOnFieldChanges() {
|
||||
//Listen on field's changes
|
||||
_fieldListener.start(onFieldsChanged: (result) {
|
||||
result.fold(
|
||||
(changeset) {
|
||||
_deleteFields(changeset.deletedFields);
|
||||
_insertFields(changeset.insertedFields);
|
||||
|
||||
final updateFields = _updateFields(changeset.updatedFields);
|
||||
for (final listener in _updatedFieldCallbacks.values) {
|
||||
listener(updateFields);
|
||||
}
|
||||
},
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void _updateSettingConfiguration(GridSettingPB setting) {
|
||||
_groupConfigurationByFieldId.clear();
|
||||
for (final configuration in setting.groupConfigurations.items) {
|
||||
_groupConfigurationByFieldId[configuration.fieldId] = configuration;
|
||||
}
|
||||
|
||||
for (final configuration in setting.filters.items) {
|
||||
_filterPBByFieldId[configuration.fieldId] = configuration;
|
||||
}
|
||||
|
||||
_updateFieldInfos();
|
||||
}
|
||||
|
||||
void _updateFieldInfos() {
|
||||
if (_fieldNotifier != null) {
|
||||
for (var field in _fieldNotifier!.fieldInfos) {
|
||||
field._isGroupField = _groupConfigurationByFieldId[field.id] != null;
|
||||
field._hasFilter = _filterPBByFieldId[field.id] != null;
|
||||
}
|
||||
_fieldNotifier?.notify();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
await _fieldListener.stop();
|
||||
await _filterListener.stop();
|
||||
await _settingListener.stop();
|
||||
|
||||
for (final callback in _fieldCallbacks.values) {
|
||||
_fieldNotifier?.removeListener(callback);
|
||||
}
|
||||
_fieldNotifier?.dispose();
|
||||
_fieldNotifier = null;
|
||||
|
||||
for (final callback in _filterCallbacks.values) {
|
||||
_filterNotifier?.removeListener(callback);
|
||||
}
|
||||
_filterNotifier?.dispose();
|
||||
_filterNotifier = null;
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> loadFields({
|
||||
required List<FieldIdPB> fieldIds,
|
||||
}) async {
|
||||
final result = await _gridFFIService.getFields(fieldIds: fieldIds);
|
||||
return Future(
|
||||
() => result.fold(
|
||||
(newFields) {
|
||||
_fieldNotifier?.fieldInfos =
|
||||
newFields.map((field) => FieldInfo(field: field)).toList();
|
||||
_loadFilters();
|
||||
_updateFieldInfos();
|
||||
return left(unit);
|
||||
},
|
||||
(err) => right(err),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> _loadFilters() async {
|
||||
return _filterFFIService.getAllFilters().then((result) {
|
||||
return result.fold(
|
||||
(filterPBs) {
|
||||
final List<FilterInfo> filters = [];
|
||||
for (final filterPB in filterPBs) {
|
||||
final fieldInfo = _findFieldInfoForFilter(fieldInfos, filterPB);
|
||||
if (fieldInfo != null) {
|
||||
final filterInfo = FilterInfo(gridId, filterPB, fieldInfo);
|
||||
filters.add(filterInfo);
|
||||
}
|
||||
}
|
||||
|
||||
_updateFieldInfos();
|
||||
_filterNotifier?.filters = filters;
|
||||
return left(unit);
|
||||
},
|
||||
(err) => right(err),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void addListener({
|
||||
OnReceiveFields? onFields,
|
||||
OnReceiveUpdateFields? onFieldsUpdated,
|
||||
OnReceiveFilters? onFilters,
|
||||
bool Function()? listenWhen,
|
||||
}) {
|
||||
if (onFieldsUpdated != null) {
|
||||
callback(List<FieldInfo> updateFields) {
|
||||
if (listenWhen != null && listenWhen() == false) {
|
||||
return;
|
||||
}
|
||||
onFieldsUpdated(updateFields);
|
||||
}
|
||||
|
||||
_updatedFieldCallbacks[onFieldsUpdated] = callback;
|
||||
}
|
||||
|
||||
if (onFields != null) {
|
||||
callback() {
|
||||
if (listenWhen != null && listenWhen() == false) {
|
||||
return;
|
||||
}
|
||||
onFields(fieldInfos);
|
||||
}
|
||||
|
||||
_fieldCallbacks[onFields] = callback;
|
||||
_fieldNotifier?.addListener(callback);
|
||||
}
|
||||
|
||||
if (onFilters != null) {
|
||||
callback() {
|
||||
if (listenWhen != null && listenWhen() == false) {
|
||||
return;
|
||||
}
|
||||
onFilters(filterInfos);
|
||||
}
|
||||
|
||||
_filterCallbacks[onFilters] = callback;
|
||||
_filterNotifier?.addListener(callback);
|
||||
}
|
||||
}
|
||||
|
||||
void removeListener({
|
||||
OnReceiveFields? onFieldsListener,
|
||||
OnReceiveFilters? onFiltersListener,
|
||||
OnReceiveUpdateFields? onChangesetListener,
|
||||
}) {
|
||||
if (onFieldsListener != null) {
|
||||
final callback = _fieldCallbacks.remove(onFieldsListener);
|
||||
if (callback != null) {
|
||||
_fieldNotifier?.removeListener(callback);
|
||||
}
|
||||
}
|
||||
if (onFiltersListener != null) {
|
||||
final callback = _filterCallbacks.remove(onFiltersListener);
|
||||
if (callback != null) {
|
||||
_filterNotifier?.removeListener(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _deleteFields(List<FieldIdPB> deletedFields) {
|
||||
if (deletedFields.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final List<FieldInfo> newFields = fieldInfos;
|
||||
final Map<String, FieldIdPB> deletedFieldMap = {
|
||||
for (var fieldOrder in deletedFields) fieldOrder.fieldId: fieldOrder
|
||||
};
|
||||
|
||||
newFields.retainWhere((field) => (deletedFieldMap[field.id] == null));
|
||||
_fieldNotifier?.fieldInfos = newFields;
|
||||
}
|
||||
|
||||
void _insertFields(List<IndexFieldPB> insertedFields) {
|
||||
if (insertedFields.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final List<FieldInfo> newFields = fieldInfos;
|
||||
for (final indexField in insertedFields) {
|
||||
final gridField = FieldInfo(field: indexField.field_1);
|
||||
if (newFields.length > indexField.index) {
|
||||
newFields.insert(indexField.index, gridField);
|
||||
} else {
|
||||
newFields.add(gridField);
|
||||
}
|
||||
}
|
||||
_fieldNotifier?.fieldInfos = newFields;
|
||||
}
|
||||
|
||||
List<FieldInfo> _updateFields(List<FieldPB> updatedFieldPBs) {
|
||||
if (updatedFieldPBs.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
|
||||
final List<FieldInfo> newFields = fieldInfos;
|
||||
final List<FieldInfo> updatedFields = [];
|
||||
for (final updatedFieldPB in updatedFieldPBs) {
|
||||
final index =
|
||||
newFields.indexWhere((field) => field.id == updatedFieldPB.id);
|
||||
if (index != -1) {
|
||||
newFields.removeAt(index);
|
||||
final fieldInfo = FieldInfo(field: updatedFieldPB);
|
||||
newFields.insert(index, fieldInfo);
|
||||
updatedFields.add(fieldInfo);
|
||||
}
|
||||
}
|
||||
|
||||
if (updatedFields.isNotEmpty) {
|
||||
_fieldNotifier?.fieldInfos = newFields;
|
||||
}
|
||||
return updatedFields;
|
||||
}
|
||||
}
|
||||
|
||||
class GridRowFieldNotifierImpl extends IGridRowFieldNotifier {
|
||||
final GridFieldController _cache;
|
||||
OnReceiveUpdateFields? _onChangesetFn;
|
||||
OnReceiveFields? _onFieldFn;
|
||||
GridRowFieldNotifierImpl(GridFieldController cache) : _cache = cache;
|
||||
|
||||
@override
|
||||
UnmodifiableListView<FieldInfo> get fields =>
|
||||
UnmodifiableListView(_cache.fieldInfos);
|
||||
|
||||
@override
|
||||
void onRowFieldsChanged(VoidCallback callback) {
|
||||
_onFieldFn = (_) => callback();
|
||||
_cache.addListener(onFields: _onFieldFn);
|
||||
}
|
||||
|
||||
@override
|
||||
void onRowFieldChanged(void Function(FieldInfo) callback) {
|
||||
_onChangesetFn = (List<FieldInfo> fieldInfos) {
|
||||
for (final updatedField in fieldInfos) {
|
||||
callback(updatedField);
|
||||
}
|
||||
};
|
||||
|
||||
_cache.addListener(onFieldsUpdated: _onChangesetFn);
|
||||
}
|
||||
|
||||
@override
|
||||
void onRowDispose() {
|
||||
if (_onFieldFn != null) {
|
||||
_cache.removeListener(onFieldsListener: _onFieldFn!);
|
||||
_onFieldFn = null;
|
||||
}
|
||||
|
||||
if (_onChangesetFn != null) {
|
||||
_cache.removeListener(onChangesetListener: _onChangesetFn!);
|
||||
_onChangesetFn = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FieldInfo? _findFieldInfoForFilter(
|
||||
List<FieldInfo> fieldInfos, FilterPB filter) {
|
||||
final fieldIndex = fieldInfos.indexWhere((element) {
|
||||
return element.id == filter.fieldId &&
|
||||
element.fieldType == filter.fieldType;
|
||||
});
|
||||
if (fieldIndex != -1) {
|
||||
return fieldInfos[fieldIndex];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class FieldInfo {
|
||||
final FieldPB _field;
|
||||
bool _isGroupField = false;
|
||||
|
||||
bool _hasFilter = false;
|
||||
|
||||
String get id => _field.id;
|
||||
|
||||
FieldType get fieldType => _field.fieldType;
|
||||
|
||||
bool get visibility => _field.visibility;
|
||||
|
||||
double get width => _field.width.toDouble();
|
||||
|
||||
bool get isPrimary => _field.isPrimary;
|
||||
|
||||
String get name => _field.name;
|
||||
|
||||
FieldPB get field => _field;
|
||||
|
||||
bool get isGroupField => _isGroupField;
|
||||
|
||||
bool get hasFilter => _hasFilter;
|
||||
|
||||
bool get canGroup {
|
||||
switch (_field.fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
return true;
|
||||
case FieldType.DateTime:
|
||||
return false;
|
||||
case FieldType.MultiSelect:
|
||||
return true;
|
||||
case FieldType.Number:
|
||||
return false;
|
||||
case FieldType.RichText:
|
||||
return false;
|
||||
case FieldType.SingleSelect:
|
||||
return true;
|
||||
case FieldType.URL:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool get canCreateFilter {
|
||||
if (hasFilter) return false;
|
||||
|
||||
switch (_field.fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
// case FieldType.MultiSelect:
|
||||
case FieldType.RichText:
|
||||
case FieldType.SingleSelect:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
FieldInfo({required FieldPB field}) : _field = field;
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
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:flowy_infra/notifier.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||
|
||||
typedef UpdateFieldNotifiedValue = Either<FieldPB, FlowyError>;
|
||||
|
||||
class SingleFieldListener {
|
||||
final String fieldId;
|
||||
PublishNotifier<UpdateFieldNotifiedValue>? _updateFieldNotifier =
|
||||
PublishNotifier();
|
||||
GridNotificationListener? _listener;
|
||||
|
||||
SingleFieldListener({required this.fieldId});
|
||||
|
||||
void start(
|
||||
{required void Function(UpdateFieldNotifiedValue) onFieldChanged}) {
|
||||
_updateFieldNotifier?.addPublishListener(onFieldChanged);
|
||||
_listener = GridNotificationListener(
|
||||
objectId: fieldId,
|
||||
handler: _handler,
|
||||
);
|
||||
}
|
||||
|
||||
void _handler(
|
||||
GridDartNotification ty,
|
||||
Either<Uint8List, FlowyError> result,
|
||||
) {
|
||||
switch (ty) {
|
||||
case GridDartNotification.DidUpdateField:
|
||||
result.fold(
|
||||
(payload) =>
|
||||
_updateFieldNotifier?.value = left(FieldPB.fromBuffer(payload)),
|
||||
(error) => _updateFieldNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
await _listener?.stop();
|
||||
_updateFieldNotifier?.dispose();
|
||||
_updateFieldNotifier = null;
|
||||
}
|
||||
}
|
||||
Whitespace-only changes.
@@ -1,47 +0,0 @@
|
||||
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:flowy_infra/notifier.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||
|
||||
typedef UpdateFieldNotifiedValue = Either<GridFieldChangesetPB, FlowyError>;
|
||||
|
||||
class GridFieldsListener {
|
||||
final String gridId;
|
||||
PublishNotifier<UpdateFieldNotifiedValue>? updateFieldsNotifier =
|
||||
PublishNotifier();
|
||||
GridNotificationListener? _listener;
|
||||
GridFieldsListener({required this.gridId});
|
||||
|
||||
void start(
|
||||
{required void Function(UpdateFieldNotifiedValue) onFieldsChanged}) {
|
||||
updateFieldsNotifier?.addPublishListener(onFieldsChanged);
|
||||
_listener = GridNotificationListener(
|
||||
objectId: gridId,
|
||||
handler: _handler,
|
||||
);
|
||||
}
|
||||
|
||||
void _handler(GridDartNotification ty, Either<Uint8List, FlowyError> result) {
|
||||
switch (ty) {
|
||||
case GridDartNotification.DidUpdateGridField:
|
||||
result.fold(
|
||||
(payload) => updateFieldsNotifier?.value =
|
||||
left(GridFieldChangesetPB.fromBuffer(payload)),
|
||||
(error) => updateFieldsNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
await _listener?.stop();
|
||||
updateFieldsNotifier?.dispose();
|
||||
updateFieldsNotifier = null;
|
||||
}
|
||||
}
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart';
|
||||
|
||||
class TypeOptionFFIService {
|
||||
final String gridId;
|
||||
final String fieldId;
|
||||
|
||||
TypeOptionFFIService({
|
||||
required this.gridId,
|
||||
required this.fieldId,
|
||||
});
|
||||
|
||||
Future<Either<SelectOptionPB, FlowyError>> newOption({
|
||||
required String name,
|
||||
}) {
|
||||
final payload = CreateSelectOptionPayloadPB.create()
|
||||
..optionName = name
|
||||
..gridId = gridId
|
||||
..fieldId = fieldId;
|
||||
|
||||
return GridEventNewSelectOption(payload).send();
|
||||
}
|
||||
}
|
||||
Whitespace-only changes.
@@ -1,120 +0,0 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:app_flowy/plugins/grid/presentation/widgets/filter/filter_info.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/block_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
|
||||
import 'dart:async';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'block/block_cache.dart';
|
||||
import 'field/field_controller.dart';
|
||||
import 'prelude.dart';
|
||||
import 'row/row_cache.dart';
|
||||
|
||||
typedef OnFieldsChanged = void Function(List<FieldInfo>);
|
||||
typedef OnFiltersChanged = void Function(List<FilterInfo>);
|
||||
typedef OnGridChanged = void Function(GridPB);
|
||||
|
||||
typedef OnRowsChanged = void Function(
|
||||
List<RowInfo> rowInfos,
|
||||
RowsChangedReason,
|
||||
);
|
||||
typedef ListenOnRowChangedCondition = bool Function();
|
||||
|
||||
class GridController {
|
||||
final String gridId;
|
||||
final GridFFIService _gridFFIService;
|
||||
final GridFieldController fieldController;
|
||||
OnRowsChanged? _onRowChanged;
|
||||
OnGridChanged? _onGridChanged;
|
||||
|
||||
// Getters
|
||||
// key: the block id
|
||||
final LinkedHashMap<String, GridBlockCache> _blocks;
|
||||
UnmodifiableMapView<String, GridBlockCache> get blocks =>
|
||||
UnmodifiableMapView(_blocks);
|
||||
|
||||
List<RowInfo> get rowInfos {
|
||||
final List<RowInfo> rows = [];
|
||||
for (var block in _blocks.values) {
|
||||
rows.addAll(block.rows);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
GridController({required ViewPB view})
|
||||
: gridId = view.id,
|
||||
// ignore: prefer_collection_literals
|
||||
_blocks = LinkedHashMap(),
|
||||
_gridFFIService = GridFFIService(gridId: view.id),
|
||||
fieldController = GridFieldController(gridId: view.id);
|
||||
|
||||
void addListener({
|
||||
OnGridChanged? onGridChanged,
|
||||
OnRowsChanged? onRowsChanged,
|
||||
OnFieldsChanged? onFieldsChanged,
|
||||
OnFiltersChanged? onFiltersChanged,
|
||||
}) {
|
||||
_onGridChanged = onGridChanged;
|
||||
_onRowChanged = onRowsChanged;
|
||||
|
||||
fieldController.addListener(
|
||||
onFields: onFieldsChanged,
|
||||
onFilters: onFiltersChanged,
|
||||
);
|
||||
}
|
||||
|
||||
// Loads the rows from each block
|
||||
Future<Either<Unit, FlowyError>> openGrid() async {
|
||||
return _gridFFIService.openGrid().then((result) {
|
||||
return result.fold(
|
||||
(grid) async {
|
||||
_initialBlocks(grid.blocks);
|
||||
_onGridChanged?.call(grid);
|
||||
|
||||
final result = await fieldController.loadFields(
|
||||
fieldIds: grid.fields,
|
||||
);
|
||||
return result;
|
||||
},
|
||||
(err) => right(err),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> createRow() async {
|
||||
await _gridFFIService.createRow();
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
await _gridFFIService.closeGrid();
|
||||
await fieldController.dispose();
|
||||
|
||||
for (final blockCache in _blocks.values) {
|
||||
blockCache.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
void _initialBlocks(List<BlockPB> blocks) {
|
||||
for (final block in blocks) {
|
||||
if (_blocks[block.id] != null) {
|
||||
Log.warn("Initial duplicate block's cache: ${block.id}");
|
||||
return;
|
||||
}
|
||||
|
||||
final cache = GridBlockCache(
|
||||
gridId: gridId,
|
||||
block: block,
|
||||
fieldController: fieldController,
|
||||
);
|
||||
|
||||
cache.addListener(onRowsChanged: (reason) {
|
||||
_onRowChanged?.call(rowInfos, reason);
|
||||
});
|
||||
|
||||
_blocks[block.id] = cache;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
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-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:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
|
||||
|
||||
class GridFFIService {
|
||||
final String gridId;
|
||||
GridFFIService({
|
||||
required this.gridId,
|
||||
});
|
||||
|
||||
Future<Either<GridPB, FlowyError>> openGrid() async {
|
||||
await FolderEventSetLatestView(ViewIdPB(value: gridId)).send();
|
||||
|
||||
final payload = GridIdPB(value: gridId);
|
||||
return GridEventGetGrid(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<RowPB, FlowyError>> createRow({Option<String>? startRowId}) {
|
||||
var payload = CreateTableRowPayloadPB.create()..gridId = gridId;
|
||||
startRowId?.fold(() => null, (id) => payload.startRowId = id);
|
||||
return GridEventCreateTableRow(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<RowPB, FlowyError>> createBoardCard(
|
||||
String groupId,
|
||||
String? startRowId,
|
||||
) {
|
||||
CreateBoardCardPayloadPB payload = CreateBoardCardPayloadPB.create()
|
||||
..gridId = gridId
|
||||
..groupId = groupId;
|
||||
|
||||
if (startRowId != null) {
|
||||
payload.startRowId = startRowId;
|
||||
}
|
||||
|
||||
return GridEventCreateBoardCard(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<List<FieldPB>, FlowyError>> getFields(
|
||||
{List<FieldIdPB>? fieldIds}) {
|
||||
var payload = GetFieldPayloadPB.create()..gridId = gridId;
|
||||
|
||||
if (fieldIds != null) {
|
||||
payload.fieldIds = RepeatedFieldIdPB(items: fieldIds);
|
||||
}
|
||||
return GridEventGetFields(payload).send().then((result) {
|
||||
return result.fold((l) => left(l.items), (r) => right(r));
|
||||
});
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> closeGrid() {
|
||||
final request = ViewIdPB(value: gridId);
|
||||
return FolderEventCloseView(request).send();
|
||||
}
|
||||
|
||||
Future<Either<RepeatedGroupPB, FlowyError>> loadGroups() {
|
||||
final payload = GridIdPB(value: gridId);
|
||||
return GridEventGetGroup(payload).send();
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
export 'grid_bloc.dart';
|
||||
export 'row/row_bloc.dart';
|
||||
export 'row/row_service.dart';
|
||||
export 'grid_service.dart';
|
||||
export 'grid_header_bloc.dart';
|
||||
|
||||
// FieldPB
|
||||
export 'field/field_service.dart';
|
||||
export 'field/field_action_sheet_bloc.dart';
|
||||
export 'field/field_editor_bloc.dart';
|
||||
export 'field/field_type_option_edit_bloc.dart';
|
||||
|
||||
// FieldPB Type Option
|
||||
export 'field/type_option/date_bloc.dart';
|
||||
export 'field/type_option/number_bloc.dart';
|
||||
export 'field/type_option/single_select_type_option.dart';
|
||||
|
||||
// CellPB
|
||||
export 'cell/text_cell_bloc.dart';
|
||||
export 'cell/number_cell_bloc.dart';
|
||||
export 'cell/select_option_cell_bloc.dart';
|
||||
export 'cell/date_cell_bloc.dart';
|
||||
export 'cell/checkbox_cell_bloc.dart';
|
||||
export 'cell/cell_service/cell_service.dart';
|
||||
|
||||
// Setting
|
||||
export 'setting/setting_bloc.dart';
|
||||
export 'setting/property_bloc.dart';
|
||||
@@ -1,49 +0,0 @@
|
||||
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_field_notifier.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../presentation/widgets/cell/cell_builder.dart';
|
||||
import '../cell/cell_service/cell_service.dart';
|
||||
import '../field/field_controller.dart';
|
||||
import 'row_cache.dart';
|
||||
|
||||
typedef OnRowChanged = void Function(GridCellMap, RowsChangedReason);
|
||||
|
||||
class GridRowDataController extends GridCellBuilderDelegate {
|
||||
final RowInfo rowInfo;
|
||||
final List<VoidCallback> _onRowChangedListeners = [];
|
||||
final GridFieldController _fieldController;
|
||||
final GridRowCache _rowCache;
|
||||
|
||||
GridRowDataController({
|
||||
required this.rowInfo,
|
||||
required GridFieldController fieldController,
|
||||
required GridRowCache rowCache,
|
||||
}) : _fieldController = fieldController,
|
||||
_rowCache = rowCache;
|
||||
|
||||
GridCellMap loadData() {
|
||||
return _rowCache.loadGridCells(rowInfo.rowPB.id);
|
||||
}
|
||||
|
||||
void addListener({OnRowChanged? onRowChanged}) {
|
||||
_onRowChangedListeners.add(_rowCache.addListener(
|
||||
rowId: rowInfo.rowPB.id,
|
||||
onCellUpdated: onRowChanged,
|
||||
));
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
for (final fn in _onRowChangedListeners) {
|
||||
_rowCache.removeRowListener(fn);
|
||||
}
|
||||
}
|
||||
|
||||
// GridCellBuilderDelegate implementation
|
||||
@override
|
||||
GridCellFieldNotifier buildFieldNotifier() {
|
||||
return GridCellFieldNotifier(
|
||||
notifier: GridCellFieldNotifierImpl(_fieldController));
|
||||
}
|
||||
|
||||
@override
|
||||
GridCellCache get cellCache => _rowCache.cellCache;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import 'package:app_flowy/core/grid_notification.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';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||
|
||||
typedef UpdateRowNotifiedValue = Either<RowPB, FlowyError>;
|
||||
typedef UpdateFieldNotifiedValue = Either<List<FieldPB>, FlowyError>;
|
||||
|
||||
class RowListener {
|
||||
final String rowId;
|
||||
PublishNotifier<UpdateRowNotifiedValue>? updateRowNotifier =
|
||||
PublishNotifier();
|
||||
GridNotificationListener? _listener;
|
||||
|
||||
RowListener({required this.rowId});
|
||||
|
||||
void start() {
|
||||
_listener = GridNotificationListener(objectId: rowId, handler: _handler);
|
||||
}
|
||||
|
||||
void _handler(GridDartNotification ty, Either<Uint8List, FlowyError> result) {
|
||||
switch (ty) {
|
||||
case GridDartNotification.DidUpdateRow:
|
||||
result.fold(
|
||||
(payload) =>
|
||||
updateRowNotifier?.value = left(RowPB.fromBuffer(payload)),
|
||||
(error) => updateRowNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
await _listener?.stop();
|
||||
updateRowNotifier?.dispose();
|
||||
updateRowNotifier = null;
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/group_changeset.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
|
||||
|
||||
class RowFFIService {
|
||||
final String gridId;
|
||||
final String blockId;
|
||||
|
||||
RowFFIService({
|
||||
required this.gridId,
|
||||
required this.blockId,
|
||||
});
|
||||
|
||||
Future<Either<RowPB, FlowyError>> createRow(String rowId) {
|
||||
final payload = CreateTableRowPayloadPB.create()
|
||||
..gridId = gridId
|
||||
..startRowId = rowId;
|
||||
|
||||
return GridEventCreateTableRow(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<OptionalRowPB, FlowyError>> getRow(String rowId) {
|
||||
final payload = RowIdPB.create()
|
||||
..gridId = gridId
|
||||
..blockId = blockId
|
||||
..rowId = rowId;
|
||||
|
||||
return GridEventGetRow(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> deleteRow(String rowId) {
|
||||
final payload = RowIdPB.create()
|
||||
..gridId = gridId
|
||||
..blockId = blockId
|
||||
..rowId = rowId;
|
||||
|
||||
return GridEventDeleteRow(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> duplicateRow(String rowId) {
|
||||
final payload = RowIdPB.create()
|
||||
..gridId = gridId
|
||||
..blockId = blockId
|
||||
..rowId = rowId;
|
||||
|
||||
return GridEventDuplicateRow(payload).send();
|
||||
}
|
||||
}
|
||||
|
||||
class MoveRowFFIService {
|
||||
final String gridId;
|
||||
|
||||
MoveRowFFIService({
|
||||
required this.gridId,
|
||||
});
|
||||
|
||||
Future<Either<Unit, FlowyError>> moveRow({
|
||||
required String fromRowId,
|
||||
required String toRowId,
|
||||
}) {
|
||||
var payload = MoveRowPayloadPB.create()
|
||||
..viewId = gridId
|
||||
..fromRowId = fromRowId
|
||||
..toRowId = toRowId;
|
||||
|
||||
return GridEventMoveRow(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> moveGroupRow({
|
||||
required String fromRowId,
|
||||
required String toGroupId,
|
||||
required String? toRowId,
|
||||
}) {
|
||||
var payload = MoveGroupRowPayloadPB.create()
|
||||
..viewId = gridId
|
||||
..fromRowId = fromRowId
|
||||
..toGroupId = toGroupId;
|
||||
|
||||
if (toRowId != null) {
|
||||
payload.toRowId = toRowId;
|
||||
}
|
||||
|
||||
return GridEventMoveGroupRow(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> moveGroup({
|
||||
required String fromGroupId,
|
||||
required String toGroupId,
|
||||
}) {
|
||||
final payload = MoveGroupPayloadPB.create()
|
||||
..viewId = gridId
|
||||
..fromGroupId = fromGroupId
|
||||
..toGroupId = toGroupId;
|
||||
|
||||
return GridEventMoveGroup(payload).send();
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
|
||||
import '../field/field_controller.dart';
|
||||
import 'setting_service.dart';
|
||||
|
||||
part 'group_bloc.freezed.dart';
|
||||
|
||||
class GridGroupBloc extends Bloc<GridGroupEvent, GridGroupState> {
|
||||
final GridFieldController _fieldController;
|
||||
final SettingFFIService _settingFFIService;
|
||||
Function(List<FieldInfo>)? _onFieldsFn;
|
||||
|
||||
GridGroupBloc({
|
||||
required String viewId,
|
||||
required GridFieldController fieldController,
|
||||
}) : _fieldController = fieldController,
|
||||
_settingFFIService = SettingFFIService(viewId: viewId),
|
||||
super(GridGroupState.initial(viewId, fieldController.fieldInfos)) {
|
||||
on<GridGroupEvent>(
|
||||
(event, emit) async {
|
||||
event.when(
|
||||
initial: () {
|
||||
_startListening();
|
||||
},
|
||||
didReceiveFieldUpdate: (fieldContexts) {
|
||||
emit(state.copyWith(fieldContexts: fieldContexts));
|
||||
},
|
||||
setGroupByField: (String fieldId, FieldType fieldType) async {
|
||||
final result = await _settingFFIService.groupByField(
|
||||
fieldId: fieldId,
|
||||
fieldType: fieldType,
|
||||
);
|
||||
result.fold((l) => null, (err) => Log.error(err));
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
if (_onFieldsFn != null) {
|
||||
_fieldController.removeListener(onFieldsListener: _onFieldsFn!);
|
||||
_onFieldsFn = null;
|
||||
}
|
||||
return super.close();
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
_onFieldsFn = (fieldContexts) =>
|
||||
add(GridGroupEvent.didReceiveFieldUpdate(fieldContexts));
|
||||
_fieldController.addListener(
|
||||
onFields: _onFieldsFn,
|
||||
listenWhen: () => !isClosed,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class GridGroupEvent with _$GridGroupEvent {
|
||||
const factory GridGroupEvent.initial() = _Initial;
|
||||
const factory GridGroupEvent.setGroupByField(
|
||||
String fieldId,
|
||||
FieldType fieldType,
|
||||
) = _GroupByField;
|
||||
const factory GridGroupEvent.didReceiveFieldUpdate(List<FieldInfo> fields) =
|
||||
_DidReceiveFieldUpdate;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class GridGroupState with _$GridGroupState {
|
||||
const factory GridGroupState({
|
||||
required String gridId,
|
||||
required List<FieldInfo> fieldContexts,
|
||||
}) = _GridGroupState;
|
||||
|
||||
factory GridGroupState.initial(
|
||||
String gridId, List<FieldInfo> fieldContexts) =>
|
||||
GridGroupState(
|
||||
gridId: gridId,
|
||||
fieldContexts: fieldContexts,
|
||||
);
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
part 'setting_bloc.freezed.dart';
|
||||
|
||||
class GridSettingBloc extends Bloc<GridSettingEvent, GridSettingState> {
|
||||
final String gridId;
|
||||
GridSettingBloc({required this.gridId}) : super(GridSettingState.initial()) {
|
||||
on<GridSettingEvent>(
|
||||
(event, emit) async {
|
||||
event.map(performAction: (_PerformAction value) {
|
||||
emit(state.copyWith(selectedAction: Some(value.action)));
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class GridSettingEvent with _$GridSettingEvent {
|
||||
const factory GridSettingEvent.performAction(GridSettingAction action) =
|
||||
_PerformAction;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class GridSettingState with _$GridSettingState {
|
||||
const factory GridSettingState({
|
||||
required Option<GridSettingAction> selectedAction,
|
||||
}) = _GridSettingState;
|
||||
|
||||
factory GridSettingState.initial() => GridSettingState(
|
||||
selectedAction: none(),
|
||||
);
|
||||
}
|
||||
|
||||
enum GridSettingAction {
|
||||
showFilters,
|
||||
sortBy,
|
||||
showProperties,
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
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/dart_notification.pbserver.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/setting_entities.pb.dart';
|
||||
|
||||
typedef UpdateSettingNotifiedValue = Either<GridSettingPB, FlowyError>;
|
||||
|
||||
class SettingListener {
|
||||
final String gridId;
|
||||
GridNotificationListener? _listener;
|
||||
PublishNotifier<UpdateSettingNotifiedValue>? _updateSettingNotifier =
|
||||
PublishNotifier();
|
||||
|
||||
SettingListener({required this.gridId});
|
||||
|
||||
void start({
|
||||
required void Function(UpdateSettingNotifiedValue) onSettingUpdated,
|
||||
}) {
|
||||
_updateSettingNotifier?.addPublishListener(onSettingUpdated);
|
||||
_listener = GridNotificationListener(objectId: gridId, handler: _handler);
|
||||
}
|
||||
|
||||
void _handler(GridDartNotification ty, Either<Uint8List, FlowyError> result) {
|
||||
switch (ty) {
|
||||
case GridDartNotification.DidUpdateGridSetting:
|
||||
result.fold(
|
||||
(payload) => _updateSettingNotifier?.value = left(
|
||||
GridSettingPB.fromBuffer(payload),
|
||||
),
|
||||
(error) => _updateSettingNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
await _listener?.stop();
|
||||
_updateSettingNotifier?.dispose();
|
||||
_updateSettingNotifier = null;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/setting_entities.pb.dart';
|
||||
|
||||
class SettingFFIService {
|
||||
final String viewId;
|
||||
|
||||
const SettingFFIService({required this.viewId});
|
||||
|
||||
Future<Either<GridSettingPB, FlowyError>> getSetting() {
|
||||
final payload = GridIdPB.create()..value = viewId;
|
||||
return GridEventGetGridSetting(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> groupByField({
|
||||
required String fieldId,
|
||||
required FieldType fieldType,
|
||||
}) {
|
||||
final insertGroupPayload = InsertGroupPayloadPB.create()
|
||||
..fieldId = fieldId
|
||||
..fieldType = fieldType;
|
||||
final payload = GridSettingChangesetPB.create()
|
||||
..gridId = viewId
|
||||
..insertGroup = insertGroupPayload;
|
||||
|
||||
return GridEventUpdateGridSetting(payload).send();
|
||||
}
|
||||
}
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
/// The data structor representing each selection of flowy table.
|
||||
enum FlowyTableSelectionType {
|
||||
item,
|
||||
row,
|
||||
col,
|
||||
}
|
||||
|
||||
class FlowyTableSelectionItem {
|
||||
final FlowyTableSelectionType type;
|
||||
final int? row;
|
||||
final int? column;
|
||||
|
||||
const FlowyTableSelectionItem({
|
||||
required this.type,
|
||||
this.row,
|
||||
this.column,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '$type($row, $column)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
return other is FlowyTableSelectionItem &&
|
||||
type == other.type &&
|
||||
row == other.row &&
|
||||
column == other.column;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => type.hashCode ^ row.hashCode ^ column.hashCode;
|
||||
}
|
||||
|
||||
class FlowyTableSelection {
|
||||
Set<FlowyTableSelectionItem> _items = {};
|
||||
|
||||
Set<FlowyTableSelectionItem> get items => _items;
|
||||
|
||||
FlowyTableSelection(
|
||||
this._items,
|
||||
);
|
||||
|
||||
FlowyTableSelection.combine(
|
||||
FlowyTableSelection lhs, FlowyTableSelection rhs) {
|
||||
this..combine(lhs)..combine(rhs);
|
||||
}
|
||||
|
||||
FlowyTableSelection operator +(FlowyTableSelection other) {
|
||||
return this..combine(other);
|
||||
}
|
||||
|
||||
void combine(FlowyTableSelection other) {
|
||||
var totalItems = items..union(other.items);
|
||||
final rows = totalItems
|
||||
.where((ele) => ele.type == FlowyTableSelectionType.row)
|
||||
.map((e) => e.row)
|
||||
.toSet();
|
||||
final cols = totalItems
|
||||
.where((ele) => ele.type == FlowyTableSelectionType.col)
|
||||
.map((e) => e.column)
|
||||
.toSet();
|
||||
totalItems.removeWhere((ele) {
|
||||
return ele.type == FlowyTableSelectionType.item &&
|
||||
(rows.contains(ele.row) || cols.contains(ele.column));
|
||||
});
|
||||
_items = totalItems;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export 'cell_builder.dart';
|
||||
export 'text_cell.dart';
|
||||
export 'number_cell.dart';
|
||||
export 'date_cell/date_cell.dart';
|
||||
export 'checkbox_cell.dart';
|
||||
export 'select_option_cell/select_option_cell.dart';
|
||||
export 'url_cell/url_cell.dart';
|
||||
@@ -1,112 +0,0 @@
|
||||
import 'package:flowy_infra/text_style.dart';
|
||||
import 'package:flowy_infra_ui/widget/rounded_input_field.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:textstyle_extensions/textstyle_extensions.dart';
|
||||
|
||||
class InputTextField extends StatefulWidget {
|
||||
final void Function(String)? onDone;
|
||||
final void Function(String)? onChanged;
|
||||
final void Function() onCanceled;
|
||||
final bool autoClearWhenDone;
|
||||
final String text;
|
||||
final int? maxLength;
|
||||
final FocusNode? focusNode;
|
||||
|
||||
const InputTextField({
|
||||
required this.text,
|
||||
this.onDone,
|
||||
required this.onCanceled,
|
||||
this.onChanged,
|
||||
this.autoClearWhenDone = false,
|
||||
this.maxLength,
|
||||
this.focusNode,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<InputTextField> createState() => _InputTextFieldState();
|
||||
}
|
||||
|
||||
class _InputTextFieldState extends State<InputTextField> {
|
||||
late FocusNode _focusNode;
|
||||
var isEdited = false;
|
||||
late TextEditingController _controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_focusNode = widget.focusNode ?? FocusNode();
|
||||
_controller = TextEditingController(text: widget.text);
|
||||
SchedulerBinding.instance.addPostFrameCallback((Duration _) {
|
||||
_focusNode.requestFocus();
|
||||
});
|
||||
|
||||
_focusNode.addListener(notifyDidEndEditing);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final height = widget.maxLength == null ? 36.0 : 56.0;
|
||||
|
||||
return RoundedInputField(
|
||||
controller: _controller,
|
||||
focusNode: _focusNode,
|
||||
autoFocus: true,
|
||||
height: height,
|
||||
maxLength: widget.maxLength,
|
||||
style: TextStyles.body1.size(13),
|
||||
onChanged: (text) {
|
||||
if (widget.onChanged != null) {
|
||||
widget.onChanged!(text);
|
||||
}
|
||||
},
|
||||
onEditingComplete: (_) {
|
||||
if (widget.onDone != null) {
|
||||
widget.onDone!(_controller.text);
|
||||
}
|
||||
|
||||
if (widget.autoClearWhenDone) {
|
||||
_controller.text = "";
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_focusNode.removeListener(notifyDidEndEditing);
|
||||
// only dispose the focusNode if it was created in this widget's initState
|
||||
if (widget.focusNode == null) {
|
||||
_focusNode.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void notifyDidEndEditing() {
|
||||
if (!_focusNode.hasFocus) {
|
||||
if (_controller.text.isEmpty) {
|
||||
widget.onCanceled();
|
||||
} else {
|
||||
if (widget.onDone != null) {
|
||||
widget.onDone!(_controller.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TypeOptionSeparator extends StatelessWidget {
|
||||
const TypeOptionSeparator({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 6),
|
||||
child: Container(
|
||||
color: Theme.of(context).dividerColor,
|
||||
height: 1.0,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_filter.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/date_filter.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/select_option_filter.pbserver.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/text_filter.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/util.pb.dart';
|
||||
|
||||
class FilterInfo {
|
||||
final String viewId;
|
||||
final FilterPB filter;
|
||||
final FieldInfo field;
|
||||
|
||||
FilterInfo(this.viewId, this.filter, this.field);
|
||||
|
||||
FilterInfo copyWith({FilterPB? filter, FieldInfo? field}) {
|
||||
return FilterInfo(
|
||||
viewId,
|
||||
filter ?? this.filter,
|
||||
field ?? this.field,
|
||||
);
|
||||
}
|
||||
|
||||
DateFilterPB? dateFilter() {
|
||||
if (filter.fieldType != FieldType.DateTime) {
|
||||
return null;
|
||||
}
|
||||
return DateFilterPB.fromBuffer(filter.data);
|
||||
}
|
||||
|
||||
TextFilterPB? textFilter() {
|
||||
if (filter.fieldType != FieldType.RichText) {
|
||||
return null;
|
||||
}
|
||||
return TextFilterPB.fromBuffer(filter.data);
|
||||
}
|
||||
|
||||
CheckboxFilterPB? checkboxFilter() {
|
||||
if (filter.fieldType != FieldType.Checkbox) {
|
||||
return null;
|
||||
}
|
||||
return CheckboxFilterPB.fromBuffer(filter.data);
|
||||
}
|
||||
|
||||
SelectOptionFilterPB? selectOptionFilter() {
|
||||
if (filter.fieldType == FieldType.SingleSelect ||
|
||||
filter.fieldType == FieldType.MultiSelect) {
|
||||
return SelectOptionFilterPB.fromBuffer(filter.data);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra/text_style.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:textstyle_extensions/textstyle_extensions.dart';
|
||||
|
||||
class FilterTextField extends StatefulWidget {
|
||||
final String hintText;
|
||||
final String text;
|
||||
final void Function(String)? onChanged;
|
||||
final void Function(String)? onSubmitted;
|
||||
final bool autoFucous;
|
||||
const FilterTextField({
|
||||
this.hintText = "",
|
||||
this.text = "",
|
||||
this.onChanged,
|
||||
this.onSubmitted,
|
||||
this.autoFucous = true,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<FilterTextField> createState() => FilterTextFieldState();
|
||||
}
|
||||
|
||||
class FilterTextFieldState extends State<FilterTextField> {
|
||||
late FocusNode focusNode;
|
||||
late TextEditingController controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
focusNode = FocusNode();
|
||||
controller = TextEditingController();
|
||||
controller.text = widget.text;
|
||||
if (widget.autoFucous) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
focusNode.requestFocus();
|
||||
});
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextField(
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
onChanged: (text) {
|
||||
widget.onChanged?.call(text);
|
||||
},
|
||||
onSubmitted: (text) {
|
||||
widget.onSubmitted?.call(text);
|
||||
},
|
||||
maxLines: 1,
|
||||
style: TextStyles.body1.size(FontSizes.s12),
|
||||
decoration: InputDecoration(
|
||||
contentPadding: const EdgeInsets.all(10),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
width: 1.0,
|
||||
),
|
||||
borderRadius: Corners.s10Border,
|
||||
),
|
||||
isDense: true,
|
||||
hintText: widget.hintText,
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
width: 1.0,
|
||||
),
|
||||
borderRadius: Corners.s8Border,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
|
||||
import 'package:app_flowy/plugins/grid/presentation/widgets/header/field_editor.dart';
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/plugins/grid/application/setting/property_bloc.dart';
|
||||
import 'package:app_flowy/plugins/grid/presentation/widgets/header/field_type_extension.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.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: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 '../../../application/field/field_controller.dart';
|
||||
import '../../layout/sizes.dart';
|
||||
|
||||
class GridPropertyList extends StatefulWidget {
|
||||
final String gridId;
|
||||
final GridFieldController fieldController;
|
||||
const GridPropertyList({
|
||||
required this.gridId,
|
||||
required this.fieldController,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _GridPropertyListState();
|
||||
}
|
||||
|
||||
class _GridPropertyListState extends State<GridPropertyList> {
|
||||
late PopoverMutex _popoverMutex;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_popoverMutex = PopoverMutex();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<GridPropertyBloc>(
|
||||
param1: widget.gridId, param2: widget.fieldController)
|
||||
..add(const GridPropertyEvent.initial()),
|
||||
child: BlocBuilder<GridPropertyBloc, GridPropertyState>(
|
||||
builder: (context, state) {
|
||||
final cells = state.fieldContexts.map((field) {
|
||||
return _GridPropertyCell(
|
||||
popoverMutex: _popoverMutex,
|
||||
gridId: widget.gridId,
|
||||
fieldInfo: field,
|
||||
key: ValueKey(field.id),
|
||||
);
|
||||
}).toList();
|
||||
|
||||
return ListView.separated(
|
||||
controller: ScrollController(),
|
||||
shrinkWrap: true,
|
||||
itemCount: cells.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return cells[index];
|
||||
},
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return VSpace(GridSize.typeOptionSeparatorHeight);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _GridPropertyCell extends StatelessWidget {
|
||||
final FieldInfo fieldInfo;
|
||||
final String gridId;
|
||||
final PopoverMutex popoverMutex;
|
||||
const _GridPropertyCell({
|
||||
required this.gridId,
|
||||
required this.fieldInfo,
|
||||
required this.popoverMutex,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final checkmark = svgWidget(
|
||||
fieldInfo.visibility ? 'home/show' : 'home/hide',
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
);
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: GridSize.typeOptionItemHeight,
|
||||
child: _editFieldButton(context),
|
||||
),
|
||||
),
|
||||
FlowyIconButton(
|
||||
width: GridSize.typeOptionItemHeight,
|
||||
onPressed: () {
|
||||
context.read<GridPropertyBloc>().add(
|
||||
GridPropertyEvent.setFieldVisibility(
|
||||
fieldInfo.id, !fieldInfo.visibility));
|
||||
},
|
||||
icon: checkmark.padding(all: 6),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _editFieldButton(BuildContext context) {
|
||||
return AppFlowyPopover(
|
||||
mutex: popoverMutex,
|
||||
offset: const Offset(20, 0),
|
||||
direction: PopoverDirection.leftWithTopAligned,
|
||||
constraints: BoxConstraints.loose(const Size(240, 400)),
|
||||
child: FlowyButton(
|
||||
text: FlowyText.medium(fieldInfo.name),
|
||||
leftIcon: svgWidget(
|
||||
fieldInfo.fieldType.iconName(),
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
popupBuilder: (BuildContext context) {
|
||||
return FieldEditor(
|
||||
gridId: gridId,
|
||||
fieldName: fieldInfo.name,
|
||||
typeOptionLoader: FieldTypeOptionLoader(
|
||||
gridId: gridId,
|
||||
field: fieldInfo.field,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import 'package:app_flowy/startup/plugin/plugin.dart';
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/menu/menu.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/widget/spacing.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
|
||||
class MenuTrash extends StatelessWidget {
|
||||
const MenuTrash({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 26,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
getIt<MenuSharedState>().latestOpenView = null;
|
||||
getIt<HomeStackManager>()
|
||||
.setPlugin(makePlugin(pluginType: PluginType.trash));
|
||||
},
|
||||
child: _render(context),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _render(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: svgWidget(
|
||||
"home/trash",
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const HSpace(6),
|
||||
FlowyText.medium(LocaleKeys.trash_text.tr()),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import 'dart:io';
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:flowy_sdk/flowy_sdk.dart';
|
||||
|
||||
class InitRustSDKTask extends LaunchTask {
|
||||
@override
|
||||
LaunchTaskType get type => LaunchTaskType.dataProcessing;
|
||||
|
||||
@override
|
||||
Future<void> initialize(LaunchContext context) async {
|
||||
await appFlowyDocumentDirectory().then((directory) async {
|
||||
await context.getIt<FlowySDK>().init(directory);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<Directory> appFlowyDocumentDirectory() async {
|
||||
switch (integrationEnv()) {
|
||||
case IntegrationMode.develop:
|
||||
Directory documentsDir = await getApplicationDocumentsDirectory();
|
||||
return Directory('${documentsDir.path}/flowy_dev').create();
|
||||
case IntegrationMode.release:
|
||||
Directory documentsDir = await getApplicationDocumentsDirectory();
|
||||
return Directory('${documentsDir.path}/flowy').create();
|
||||
case IntegrationMode.test:
|
||||
return Directory("${Directory.current.path}/.sandbox");
|
||||
}
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
import 'package:app_flowy/user/application/auth_service.dart';
|
||||
import 'package:app_flowy/user/presentation/router.dart';
|
||||
import 'package:app_flowy/user/presentation/widgets/background.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra/uuid.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder/protobuf.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:dartz/dartz.dart' as dartz;
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
|
||||
class SkipLogInScreen extends StatefulWidget {
|
||||
final AuthRouter router;
|
||||
final AuthService authService;
|
||||
|
||||
const SkipLogInScreen({
|
||||
Key? key,
|
||||
required this.router,
|
||||
required this.authService,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<SkipLogInScreen> createState() => _SkipLogInScreenState();
|
||||
}
|
||||
|
||||
class _SkipLogInScreenState extends State<SkipLogInScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: SizedBox(
|
||||
width: 400,
|
||||
height: 600,
|
||||
child: _renderBody(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _renderBody(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
FlowyLogoTitle(
|
||||
title: LocaleKeys.welcomeText.tr(),
|
||||
logoSize: const Size.square(60),
|
||||
),
|
||||
const VSpace(80),
|
||||
GoButton(onPressed: () => _autoRegister(context)),
|
||||
const VSpace(30),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
InkWell(
|
||||
hoverColor: Colors.transparent,
|
||||
onTap: () =>
|
||||
_launchURL('https://github.com/AppFlowy-IO/appflowy'),
|
||||
child: FlowyText.medium(
|
||||
LocaleKeys.githubStarText.tr(),
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
hoverColor: Colors.transparent,
|
||||
onTap: () => _launchURL('https://www.appflowy.io/blog'),
|
||||
child: FlowyText.medium(
|
||||
LocaleKeys.subscribeNewsletterText.tr(),
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
_launchURL(String url) async {
|
||||
final uri = Uri.parse(url);
|
||||
if (await canLaunchUrl(uri)) {
|
||||
await launchUrl(uri);
|
||||
} else {
|
||||
throw 'Could not launch $url';
|
||||
}
|
||||
}
|
||||
|
||||
void _autoRegister(BuildContext context) async {
|
||||
const password = "AppFlowy123@";
|
||||
final uid = uuid();
|
||||
final userEmail = "$uid@appflowy.io";
|
||||
final result = await widget.authService.signUp(
|
||||
name: LocaleKeys.defaultUsername.tr(),
|
||||
password: password,
|
||||
email: userEmail,
|
||||
);
|
||||
result.fold(
|
||||
(user) {
|
||||
FolderEventReadCurrentWorkspace().send().then((result) {
|
||||
_openCurrentWorkspace(context, user, result);
|
||||
});
|
||||
},
|
||||
(error) {
|
||||
Log.error(error);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _openCurrentWorkspace(
|
||||
BuildContext context,
|
||||
UserProfilePB user,
|
||||
dartz.Either<WorkspaceSettingPB, FlowyError> workspacesOrError,
|
||||
) {
|
||||
workspacesOrError.fold(
|
||||
(workspaceSetting) {
|
||||
widget.router.pushHomeScreen(context, user, workspaceSetting);
|
||||
},
|
||||
(error) {
|
||||
Log.error(error);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GoButton extends StatelessWidget {
|
||||
final VoidCallback onPressed;
|
||||
const GoButton({
|
||||
Key? key,
|
||||
required this.onPressed,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return RoundedTextButton(
|
||||
title: LocaleKeys.letsGoButtonText.tr(),
|
||||
fontSize: FontSizes.s16,
|
||||
height: 50,
|
||||
borderRadius: Corners.s10Border,
|
||||
onPressed: onPressed,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
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-folder/app.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
|
||||
|
||||
import 'package:app_flowy/startup/plugin/plugin.dart';
|
||||
|
||||
class AppService {
|
||||
Future<Either<AppPB, FlowyError>> readApp({required String appId}) {
|
||||
final payload = AppIdPB.create()..value = appId;
|
||||
|
||||
return FolderEventReadApp(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<ViewPB, FlowyError>> createView({
|
||||
required String appId,
|
||||
required String name,
|
||||
String? desc,
|
||||
required ViewDataFormatPB dataFormatType,
|
||||
required PluginType pluginType,
|
||||
required ViewLayoutTypePB layoutType,
|
||||
}) {
|
||||
var payload = CreateViewPayloadPB.create()
|
||||
..belongToId = appId
|
||||
..name = name
|
||||
..desc = desc ?? ""
|
||||
..dataFormat = dataFormatType
|
||||
..layout = layoutType;
|
||||
|
||||
return FolderEventCreateView(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<List<ViewPB>, FlowyError>> getViews({required String appId}) {
|
||||
final payload = AppIdPB.create()..value = appId;
|
||||
|
||||
return FolderEventReadApp(payload).send().then((result) {
|
||||
return result.fold(
|
||||
(app) => left(app.belongings.items),
|
||||
(error) => right(error),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> delete({required String appId}) {
|
||||
final request = AppIdPB.create()..value = appId;
|
||||
return FolderEventDeleteApp(request).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> deleteView({required String viewId}) {
|
||||
final request = RepeatedViewIdPB.create()..items.add(viewId);
|
||||
return FolderEventDeleteView(request).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> updateApp(
|
||||
{required String appId, String? name}) {
|
||||
UpdateAppPayloadPB payload = UpdateAppPayloadPB.create()..appId = appId;
|
||||
|
||||
if (name != null) {
|
||||
payload.name = name;
|
||||
}
|
||||
return FolderEventUpdateApp(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> moveView({
|
||||
required String viewId,
|
||||
required int fromIndex,
|
||||
required int toIndex,
|
||||
}) {
|
||||
final payload = MoveFolderItemPayloadPB.create()
|
||||
..itemId = viewId
|
||||
..from = fromIndex
|
||||
..to = toIndex
|
||||
..ty = MoveFolderItemType.MoveView;
|
||||
|
||||
return FolderEventMoveFolderItem(payload).send();
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:app_flowy/user/application/user_settings_service.dart';
|
||||
import 'package:flowy_infra/theme.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-user/user_setting.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'appearance.freezed.dart';
|
||||
|
||||
/// [AppearanceSettingsCubit] is used to modify the appear setting of AppFlowy application. Includes the [Locale] and [AppTheme].
|
||||
class AppearanceSettingsCubit extends Cubit<AppearanceSettingsState> {
|
||||
final AppearanceSettingsPB _setting;
|
||||
|
||||
AppearanceSettingsCubit(AppearanceSettingsPB setting)
|
||||
: _setting = setting,
|
||||
super(AppearanceSettingsState.initial(
|
||||
setting.theme,
|
||||
setting.font,
|
||||
setting.monospaceFont,
|
||||
setting.locale,
|
||||
));
|
||||
|
||||
/// Updates the current theme and notify the listeners the theme was changed.
|
||||
/// Do nothing if the passed in themeType equal to the current theme type.
|
||||
void setTheme(Brightness brightness) {
|
||||
if (state.theme.brightness == brightness) {
|
||||
return;
|
||||
}
|
||||
|
||||
_setting.theme = themeTypeToString(brightness);
|
||||
_saveAppearanceSettings();
|
||||
|
||||
emit(state.copyWith(
|
||||
theme: AppTheme.fromName(
|
||||
themeName: _setting.theme,
|
||||
font: state.theme.font,
|
||||
monospaceFont: state.theme.monospaceFont,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
/// Updates the current locale and notify the listeners the locale was changed
|
||||
/// Fallback to [en] locale If the newLocale is not supported.
|
||||
void setLocale(BuildContext context, Locale newLocale) {
|
||||
if (!context.supportedLocales.contains(newLocale)) {
|
||||
Log.warn("Unsupported locale: $newLocale, Fallback to locale: en");
|
||||
newLocale = const Locale('en');
|
||||
}
|
||||
|
||||
context.setLocale(newLocale);
|
||||
|
||||
if (state.locale != newLocale) {
|
||||
_setting.locale.languageCode = newLocale.languageCode;
|
||||
_setting.locale.countryCode = newLocale.countryCode ?? "";
|
||||
_saveAppearanceSettings();
|
||||
|
||||
emit(state.copyWith(locale: newLocale));
|
||||
}
|
||||
}
|
||||
|
||||
/// Saves key/value setting to disk.
|
||||
/// Removes the key if the passed in value is null
|
||||
void setKeyValue(String key, String? value) {
|
||||
if (key.isEmpty) {
|
||||
Log.warn("The key should not be empty");
|
||||
return;
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
_setting.settingKeyValue.remove(key);
|
||||
}
|
||||
|
||||
if (_setting.settingKeyValue[key] != value) {
|
||||
if (value == null) {
|
||||
_setting.settingKeyValue.remove(key);
|
||||
} else {
|
||||
_setting.settingKeyValue[key] = value;
|
||||
}
|
||||
}
|
||||
_saveAppearanceSettings();
|
||||
}
|
||||
|
||||
String? getValue(String key) {
|
||||
if (key.isEmpty) {
|
||||
Log.warn("The key should not be empty");
|
||||
return null;
|
||||
}
|
||||
return _setting.settingKeyValue[key];
|
||||
}
|
||||
|
||||
/// Called when the application launch.
|
||||
/// Uses the device locale when open the application for the first time
|
||||
void readLocaleWhenAppLaunch(BuildContext context) {
|
||||
if (_setting.resetToDefault) {
|
||||
_setting.resetToDefault = false;
|
||||
_saveAppearanceSettings();
|
||||
setLocale(context, context.deviceLocale);
|
||||
return;
|
||||
}
|
||||
|
||||
setLocale(context, state.locale);
|
||||
}
|
||||
|
||||
Future<void> _saveAppearanceSettings() async {
|
||||
SettingsFFIService().setAppearanceSetting(_setting).then((result) {
|
||||
result.fold(
|
||||
(l) => null,
|
||||
(error) => Log.error(error),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class AppearanceSettingsState with _$AppearanceSettingsState {
|
||||
const factory AppearanceSettingsState({
|
||||
required AppTheme theme,
|
||||
required Locale locale,
|
||||
}) = _AppearanceSettingsState;
|
||||
|
||||
factory AppearanceSettingsState.initial(
|
||||
String themeName,
|
||||
String font,
|
||||
String monospaceFont,
|
||||
LocaleSettingsPB locale,
|
||||
) =>
|
||||
AppearanceSettingsState(
|
||||
theme: AppTheme.fromName(
|
||||
themeName: themeName,
|
||||
font: font,
|
||||
monospaceFont: monospaceFont,
|
||||
),
|
||||
locale: Locale(locale.languageCode, locale.countryCode),
|
||||
);
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import 'package:app_flowy/startup/plugin/plugin.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
class AddButton extends StatelessWidget {
|
||||
final Function(PluginBuilder) onSelected;
|
||||
|
||||
const AddButton({
|
||||
Key? key,
|
||||
required this.onSelected,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final List<PopoverAction> actions = [];
|
||||
actions.addAll(
|
||||
pluginBuilders()
|
||||
.map((pluginBuilder) =>
|
||||
AddButtonActionWrapper(pluginBuilder: pluginBuilder))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
return PopoverActionList<PopoverAction>(
|
||||
direction: PopoverDirection.bottomWithLeftAligned,
|
||||
actions: actions,
|
||||
buildChild: (controller) {
|
||||
return FlowyIconButton(
|
||||
width: 22,
|
||||
onPressed: () => controller.show(),
|
||||
icon: svgWidget(
|
||||
"home/add",
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
).padding(horizontal: 3, vertical: 3),
|
||||
);
|
||||
},
|
||||
onSelected: (action, controller) {
|
||||
if (action is AddButtonActionWrapper) {
|
||||
onSelected(action.pluginBuilder);
|
||||
}
|
||||
|
||||
controller.close();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AddButtonActionWrapper extends ActionCell {
|
||||
final PluginBuilder pluginBuilder;
|
||||
|
||||
AddButtonActionWrapper({required this.pluginBuilder});
|
||||
|
||||
@override
|
||||
Widget? leftIcon(Color iconColor) =>
|
||||
svgWidget(pluginBuilder.menuIcon, color: iconColor);
|
||||
|
||||
@override
|
||||
String get name => pluginBuilder.menuName;
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:app_flowy/workspace/application/appearance.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/toggle/toggle_style.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../widgets/toggle/toggle.dart';
|
||||
|
||||
class SettingsAppearanceView extends StatelessWidget {
|
||||
const SettingsAppearanceView({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
FlowyText.medium(LocaleKeys.settings_appearance_lightLabel.tr()),
|
||||
Toggle(
|
||||
value: Theme.of(context).brightness == Brightness.dark,
|
||||
onChanged: (_) => setTheme(context),
|
||||
style: ToggleStyle.big,
|
||||
),
|
||||
FlowyText.medium(LocaleKeys.settings_appearance_darkLabel.tr())
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void setTheme(BuildContext context) {
|
||||
if (Theme.of(context).brightness == Brightness.dark) {
|
||||
context.read<AppearanceSettingsCubit>().setTheme(Brightness.light);
|
||||
} else {
|
||||
context.read<AppearanceSettingsCubit>().setTheme(Brightness.dark);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||
import 'package:app_flowy/workspace/presentation/settings/widgets/settings_menu_element.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SettingsMenu extends StatelessWidget {
|
||||
const SettingsMenu({
|
||||
Key? key,
|
||||
required this.changeSelectedIndex,
|
||||
required this.currentIndex,
|
||||
}) : super(key: key);
|
||||
|
||||
final Function changeSelectedIndex;
|
||||
final int currentIndex;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
SettingsMenuElement(
|
||||
index: 0,
|
||||
currentIndex: currentIndex,
|
||||
label: LocaleKeys.settings_menu_appearance.tr(),
|
||||
icon: Icons.brightness_4,
|
||||
changeSelectedIndex: changeSelectedIndex,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
SettingsMenuElement(
|
||||
index: 1,
|
||||
currentIndex: currentIndex,
|
||||
label: LocaleKeys.settings_menu_language.tr(),
|
||||
icon: Icons.translate,
|
||||
changeSelectedIndex: changeSelectedIndex,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
SettingsMenuElement(
|
||||
index: 2,
|
||||
currentIndex: currentIndex,
|
||||
label: LocaleKeys.settings_menu_user.tr(),
|
||||
icon: Icons.account_box_outlined,
|
||||
changeSelectedIndex: changeSelectedIndex,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
-143
@@ -1,143 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/emoji_picker/emoji_picker.dart';
|
||||
|
||||
class FlowyEmojiStyleButton extends StatefulWidget {
|
||||
// final Attribute attribute;
|
||||
final String normalIcon;
|
||||
// TODO: enable insert emoji in appflowy_editor
|
||||
// final QuillController controller;
|
||||
final String tooltipText;
|
||||
|
||||
const FlowyEmojiStyleButton({
|
||||
// required this.attribute,
|
||||
required this.normalIcon,
|
||||
required this.tooltipText,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
EmojiStyleButtonState createState() => EmojiStyleButtonState();
|
||||
}
|
||||
|
||||
class EmojiStyleButtonState extends State<FlowyEmojiStyleButton> {
|
||||
// Style get _selectionStyle => widget.controller.getSelectionStyle();
|
||||
final GlobalKey emojiButtonKey = GlobalKey();
|
||||
OverlayEntry? _entry;
|
||||
// final FocusNode _keyFocusNode = FocusNode();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// _isToggled = _getIsToggled(_selectionStyle.attributes);
|
||||
// widget.controller.addListener(_didChangeEditingValue);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// debugPrint(MediaQuery.of(context).size.width.toString());
|
||||
// debugPrint(MediaQuery.of(context).size.height.toString());
|
||||
|
||||
// return ToolbarIconButton(
|
||||
// key: emojiButtonKey,
|
||||
// onPressed: _toggleAttribute,
|
||||
// width: widget.iconSize * kIconButtonFactor,
|
||||
// isToggled: _isToggled,
|
||||
// iconName: widget.normalIcon,
|
||||
// tooltipText: widget.tooltipText,
|
||||
// );
|
||||
return Container();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_entry?.remove();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// void _toggleAttribute() {
|
||||
// if (_entry?.mounted ?? false) {
|
||||
// _entry?.remove();
|
||||
// _entry = null;
|
||||
// setState(() => _isToggled = false);
|
||||
// } else {
|
||||
// RenderBox box =
|
||||
// emojiButtonKey.currentContext?.findRenderObject() as RenderBox;
|
||||
// Offset position = box.localToGlobal(Offset.zero);
|
||||
|
||||
// // final window = await getWindowInfo();
|
||||
|
||||
// _entry = OverlayEntry(
|
||||
// builder: (BuildContext context) => BuildEmojiPickerView(
|
||||
// controller: widget.controller,
|
||||
// offset: position,
|
||||
// ),
|
||||
// );
|
||||
|
||||
// Overlay.of(context)!.insert(_entry!);
|
||||
// setState(() => _isToggled = true);
|
||||
// }
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
class BuildEmojiPickerView extends StatefulWidget {
|
||||
const BuildEmojiPickerView({Key? key, this.offset}) : super(key: key);
|
||||
final Offset? offset;
|
||||
|
||||
@override
|
||||
State<BuildEmojiPickerView> createState() => _BuildEmojiPickerViewState();
|
||||
}
|
||||
|
||||
class _BuildEmojiPickerViewState extends State<BuildEmojiPickerView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
top: widget.offset!.dy -
|
||||
MediaQuery.of(context).size.height / 2.83 -
|
||||
30,
|
||||
left:
|
||||
widget.offset!.dx - MediaQuery.of(context).size.width / 3.92 + 40,
|
||||
child: Material(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
child: SizedBox(
|
||||
height: MediaQuery.of(context).size.height / 2.83 + 20,
|
||||
width: MediaQuery.of(context).size.width / 3.92,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
child: EmojiPicker(
|
||||
onEmojiSelected: (category, emoji) => insertEmoji(emoji),
|
||||
config: const Config(
|
||||
columns: 8,
|
||||
emojiSizeMax: 28,
|
||||
bgColor: Color(0xffF2F2F2),
|
||||
iconColor: Colors.grey,
|
||||
iconColorSelected: Color(0xff333333),
|
||||
indicatorColor: Color(0xff333333),
|
||||
progressIndicatorColor: Color(0xff333333),
|
||||
buttonMode: ButtonMode.CUPERTINO,
|
||||
initCategory: Category.RECENT,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void insertEmoji(Emoji emoji) {
|
||||
// final baseOffset = widget.controller.selection.baseOffset;
|
||||
// final extentOffset = widget.controller.selection.extentOffset;
|
||||
// final replaceLen = extentOffset - baseOffset;
|
||||
// final selection = widget.controller.selection.copyWith(
|
||||
// baseOffset: baseOffset + emoji.emoji.length,
|
||||
// extentOffset: baseOffset + emoji.emoji.length,
|
||||
// );
|
||||
|
||||
// widget.controller
|
||||
// .replaceText(baseOffset, replaceLen, emoji.emoji, selection);
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
// clang-format off
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <flowy_infra_ui/flowy_infra_u_i_plugin.h>
|
||||
#include <hotkey_manager/hotkey_manager_plugin.h>
|
||||
#include <rich_clipboard_linux/rich_clipboard_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
#include <window_size/window_size_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) flowy_infra_ui_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlowyInfraUIPlugin");
|
||||
flowy_infra_u_i_plugin_register_with_registrar(flowy_infra_ui_registrar);
|
||||
g_autoptr(FlPluginRegistrar) hotkey_manager_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "HotkeyManagerPlugin");
|
||||
hotkey_manager_plugin_register_with_registrar(hotkey_manager_registrar);
|
||||
g_autoptr(FlPluginRegistrar) rich_clipboard_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "RichClipboardPlugin");
|
||||
rich_clipboard_plugin_register_with_registrar(rich_clipboard_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) window_size_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowSizePlugin");
|
||||
window_size_plugin_register_with_registrar(window_size_registrar);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
// clang-format off
|
||||
|
||||
#ifndef GENERATED_PLUGIN_REGISTRANT_
|
||||
#define GENERATED_PLUGIN_REGISTRANT_
|
||||
|
||||
#include <flutter_linux/flutter_linux.h>
|
||||
|
||||
// Registers Flutter plugins.
|
||||
void fl_register_plugins(FlPluginRegistry* registry);
|
||||
|
||||
#endif // GENERATED_PLUGIN_REGISTRANT_
|
||||
@@ -1,28 +0,0 @@
|
||||
#
|
||||
# Generated file, do not edit.
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
flowy_infra_ui
|
||||
hotkey_manager
|
||||
rich_clipboard_linux
|
||||
url_launcher_linux
|
||||
window_size
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
||||
foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
||||
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
|
||||
endforeach(plugin)
|
||||
|
||||
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
|
||||
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
|
||||
endforeach(ffi_plugin)
|
||||
@@ -1,32 +0,0 @@
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import connectivity_plus_macos
|
||||
import device_info_plus_macos
|
||||
import flowy_infra_ui
|
||||
import flowy_sdk
|
||||
import hotkey_manager
|
||||
import package_info_plus_macos
|
||||
import path_provider_macos
|
||||
import rich_clipboard_macos
|
||||
import shared_preferences_macos
|
||||
import url_launcher_macos
|
||||
import window_size
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
|
||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||
FlowyInfraUIPlugin.register(with: registry.registrar(forPlugin: "FlowyInfraUIPlugin"))
|
||||
FlowySdkPlugin.register(with: registry.registrar(forPlugin: "FlowySdkPlugin"))
|
||||
HotkeyManagerPlugin.register(with: registry.registrar(forPlugin: "HotkeyManagerPlugin"))
|
||||
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
RichClipboardPlugin.register(with: registry.registrar(forPlugin: "RichClipboardPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
WindowSizePlugin.register(with: registry.registrar(forPlugin: "WindowSizePlugin"))
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,36 +0,0 @@
|
||||
# 0.0.9
|
||||
* Enable slide to select text in card
|
||||
* Fix some bugs
|
||||
|
||||
# 0.0.8
|
||||
* Enable drag and drop group
|
||||
|
||||
# 0.0.7
|
||||
* Rename some classes
|
||||
* Add documentation
|
||||
|
||||
# 0.0.6
|
||||
* Support scroll to bottom
|
||||
* Fix some bugs
|
||||
|
||||
# 0.0.5
|
||||
* Optimize insert card animation
|
||||
* Enable insert card at the end of the group
|
||||
* Fix some bugs
|
||||
|
||||
# 0.0.4
|
||||
* Fix some bugs
|
||||
|
||||
# 0.0.3
|
||||
* Support customize UI
|
||||
* Update example
|
||||
* Add AppFlowy style widget
|
||||
|
||||
# 0.0.2
|
||||
|
||||
* Update documentation
|
||||
|
||||
# 0.0.1
|
||||
|
||||
* Support drag and drop group items from one to another
|
||||
|
||||
@@ -1,661 +0,0 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
@@ -1,140 +0,0 @@
|
||||
# appflowy_board
|
||||
|
||||
<h1 align="center"><b>AppFlowy Board</b></h1>
|
||||
|
||||
<p align="center">A customizable and draggable Kanban Board widget for Flutter</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://discord.gg/ZCCYN4Anzq"><b>Discord</b></a> •
|
||||
<a href="https://twitter.com/appflowy"><b>Twitter</b></a>
|
||||
</p>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<img src="https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/app_flowy/packages/appflowy_board/example/gifs/appflowy_board_video_1.gif?raw=true" width="680" title="AppFlowyBoard">
|
||||
</p>
|
||||
|
||||
## Intro
|
||||
|
||||
appflowy_board is a customizable and draggable Kanban Board widget for Flutter.
|
||||
You can use it to create a Kanban Board tool like those in Trello.
|
||||
|
||||
Check out [AppFlowy](https://github.com/AppFlowy-IO/AppFlowy) to see how appflowy_board is used to build a BoardView database.
|
||||
<p align="center">
|
||||
<img src="https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/app_flowy/packages/appflowy_board/example/gifs/appflowy_board_video_2.gif?raw=true" width="680" title="AppFlowyBoard">
|
||||
</p>
|
||||
|
||||
|
||||
## Getting Started
|
||||
Add the AppFlowy Board [Flutter package](https://docs.flutter.dev/development/packages-and-plugins/using-packages) to your environment.
|
||||
|
||||
With Flutter:
|
||||
```dart
|
||||
flutter pub add appflowy_board
|
||||
flutter pub get
|
||||
```
|
||||
|
||||
This will add a line like this to your package's pubspec.yaml:
|
||||
```dart
|
||||
dependencies:
|
||||
appflowy_board: ^0.0.6
|
||||
```
|
||||
|
||||
## Create your first board
|
||||
|
||||
Initialize an `AppFlowyBoardController` for the board. It contains the data used by the board. You can
|
||||
register callbacks to receive the changes of the board.
|
||||
|
||||
```dart
|
||||
|
||||
final AppFlowyBoardController controller = AppFlowyBoardController(
|
||||
onMoveGroup: (fromGroupId, fromIndex, toGroupId, toIndex) {
|
||||
debugPrint('Move item from $fromIndex to $toIndex');
|
||||
},
|
||||
onMoveGroupItem: (groupId, fromIndex, toIndex) {
|
||||
debugPrint('Move $groupId:$fromIndex to $groupId:$toIndex');
|
||||
},
|
||||
onMoveGroupItemToGroup: (fromGroupId, fromIndex, toGroupId, toIndex) {
|
||||
debugPrint('Move $fromGroupId:$fromIndex to $toGroupId:$toIndex');
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
Provide an initial value of the board by initializing the `AppFlowyGroupData`. It represents a group data and contains list of items. Each item displayed in the group requires to implement the `AppFlowyGroupItem` class.
|
||||
|
||||
```dart
|
||||
|
||||
void initState() {
|
||||
final group1 = AppFlowyGroupData(id: "To Do", items: [
|
||||
TextItem("Card 1"),
|
||||
TextItem("Card 2"),
|
||||
]);
|
||||
final group2 = AppFlowyGroupData(id: "In Progress", items: [
|
||||
TextItem("Card 3"),
|
||||
TextItem("Card 4"),
|
||||
]);
|
||||
|
||||
final group3 = AppFlowyGroupData(id: "Done", items: []);
|
||||
|
||||
controller.addGroup(group1);
|
||||
controller.addGroup(group2);
|
||||
controller.addGroup(group3);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
class TextItem extends AppFlowyGroupItem {
|
||||
final String s;
|
||||
TextItem(this.s);
|
||||
|
||||
@override
|
||||
String get id => s;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Finally, return a `AppFlowyBoard` widget in the build method.
|
||||
|
||||
```dart
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppFlowyBoard(
|
||||
controller: controller,
|
||||
cardBuilder: (context, group, groupItem) {
|
||||
final textItem = groupItem as TextItem;
|
||||
return AppFlowyGroupCard(
|
||||
key: ObjectKey(textItem),
|
||||
child: Text(textItem.s),
|
||||
);
|
||||
},
|
||||
groupConstraints: const BoxConstraints.tightFor(width: 240),
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Usage Example
|
||||
To quickly grasp how it can be used, look at the /example/lib folder.
|
||||
First, run main.dart to play with the demo.
|
||||
|
||||
|
||||
Second, let's delve into multi_board_list_example.dart to understand a few key components:
|
||||
* A Board widget is created via instantiating an `AppFlowyBoard` object.
|
||||
* In the `AppFlowyBoard` object, you can find the `AppFlowyBoardController`, which is defined in board_data.dart, is fed with pre-populated mock data. It also contains callback functions to materialize future user data.
|
||||
* Three builders: AppFlowyBoardHeaderBuilder, AppFlowyBoardFooterBuilder, AppFlowyBoardCardBuilder. See below image for what they are used for.
|
||||
|
||||
|
||||
<p>
|
||||
<img src="https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/app_flowy/packages/appflowy_board/example/gifs/appflowy_board_builders.jpg?raw=true" width="200" title="AppFlowyBoard">
|
||||
</p>
|
||||
|
||||
## Glossary
|
||||
Please refer to the API documentation.
|
||||
|
||||
## Contributing
|
||||
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
|
||||
|
||||
Please look at [CONTRIBUTING.md](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/contributing-to-appflowy) for details.
|
||||
|
||||
## License
|
||||
Distributed under the AGPLv3 License. See [LICENSE](https://github.com/AppFlowy-IO/AppFlowy-Docs/blob/main/LICENSE) for more information.
|
||||
@@ -1,49 +0,0 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
**/ios/Flutter/.last_build_id
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
|
||||
# Web related
|
||||
lib/generated_plugin_registrant.dart
|
||||
|
||||
# Symbolication related
|
||||
app.*.symbols
|
||||
|
||||
# Obfuscation related
|
||||
app.*.map.json
|
||||
|
||||
# Android Studio will place build artifacts here
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
|
||||
.metadata
|
||||
@@ -1,71 +0,0 @@
|
||||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file('local.properties')
|
||||
if (localPropertiesFile.exists()) {
|
||||
localPropertiesFile.withReader('UTF-8') { reader ->
|
||||
localProperties.load(reader)
|
||||
}
|
||||
}
|
||||
|
||||
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||
if (flutterRoot == null) {
|
||||
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||
}
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = '1'
|
||||
}
|
||||
|
||||
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||
if (flutterVersionName == null) {
|
||||
flutterVersionName = '1.0'
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion flutter.compileSdkVersion
|
||||
ndkVersion flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "com.appflowy.board.example"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
|
||||
minSdkVersion flutter.minSdkVersion
|
||||
targetSdkVersion flutter.targetSdkVersion
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flutter {
|
||||
source '../..'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
}
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.appflowy.board.example">
|
||||
<!-- The INTERNET permission is required for development. Specifically,
|
||||
the Flutter tool needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.appflowy.board.example">
|
||||
<application
|
||||
android:label="example"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
</application>
|
||||
</manifest>
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
package com.appflowy.board.example
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
class MainActivity: FlutterActivity() {
|
||||
}
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.appflowy.board.example">
|
||||
<!-- The INTERNET permission is required for development. Specifically,
|
||||
the Flutter tool needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 87 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 69 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 291 KiB |
-481
@@ -1,481 +0,0 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 50;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||
9740EEB31CF90195004384FC /* Generated.xcconfig */,
|
||||
);
|
||||
name = Flutter;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146E51CF9000F007C117D = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9740EEB11CF90186004384FC /* Flutter */,
|
||||
97C146F01CF9000F007C117D /* Runner */,
|
||||
97C146EF1CF9000F007C117D /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146EF1CF9000F007C117D /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
97C146EE1CF9000F007C117D /* Runner.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146F01CF9000F007C117D /* Runner */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
||||
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||
97C147021CF9000F007C117D /* Info.plist */,
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
||||
);
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
97C146ED1CF9000F007C117D /* Runner */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||
buildPhases = (
|
||||
9740EEB61CF901F6004384FC /* Run Script */,
|
||||
97C146EA1CF9000F007C117D /* Sources */,
|
||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||
97C146EC1CF9000F007C117D /* Resources */,
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = Runner;
|
||||
productName = Runner;
|
||||
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
97C146E61CF9000F007C117D /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1300;
|
||||
ORGANIZATIONNAME = "";
|
||||
TargetAttributes = {
|
||||
97C146ED1CF9000F007C117D = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
LastSwiftMigration = 1100;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 97C146E51CF9000F007C117D;
|
||||
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
97C146ED1CF9000F007C117D /* Runner */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
97C146EC1CF9000F007C117D /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Thin Binary";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||
};
|
||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Run Script";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
97C146EA1CF9000F007C117D /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
97C146FB1CF9000F007C117D /* Base */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
97C147001CF9000F007C117D /* Base */,
|
||||
);
|
||||
name = LaunchScreen.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
249021D3217E4FDB00AE95B9 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
249021D4217E4FDB00AE95B9 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.board.example;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
97C147031CF9000F007C117D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
97C147041CF9000F007C117D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
97C147061CF9000F007C117D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.board.example;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
97C147071CF9000F007C117D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.board.example;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
97C147031CF9000F007C117D /* Debug */,
|
||||
97C147041CF9000F007C117D /* Release */,
|
||||
249021D3217E4FDB00AE95B9 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
97C147061CF9000F007C117D /* Debug */,
|
||||
97C147071CF9000F007C117D /* Release */,
|
||||
249021D4217E4FDB00AE95B9 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'single_board_list_example.dart';
|
||||
import 'multi_board_list_example.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatefulWidget {
|
||||
const MyApp({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<MyApp> createState() => _MyAppState();
|
||||
}
|
||||
|
||||
class _MyAppState extends State<MyApp> {
|
||||
int _currentIndex = 0;
|
||||
final _bottomNavigationColor = Colors.blue;
|
||||
|
||||
final List<Widget> _examples = [
|
||||
const MultiBoardListExample(),
|
||||
const SingleBoardListExample(),
|
||||
];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
home: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('AppFlowy Board'),
|
||||
),
|
||||
body: Container(color: Colors.white, child: _examples[_currentIndex]),
|
||||
bottomNavigationBar: BottomNavigationBar(
|
||||
fixedColor: _bottomNavigationColor,
|
||||
showSelectedLabels: true,
|
||||
showUnselectedLabels: false,
|
||||
currentIndex: _currentIndex,
|
||||
items: [
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.grid_on, color: _bottomNavigationColor),
|
||||
label: "MultiColumn"),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.grid_on, color: _bottomNavigationColor),
|
||||
label: "SingleColumn"),
|
||||
],
|
||||
onTap: (int index) {
|
||||
setState(() {
|
||||
_currentIndex = index;
|
||||
});
|
||||
},
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
import 'package:appflowy_board/appflowy_board.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MultiBoardListExample extends StatefulWidget {
|
||||
const MultiBoardListExample({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<MultiBoardListExample> createState() => _MultiBoardListExampleState();
|
||||
}
|
||||
|
||||
class _MultiBoardListExampleState extends State<MultiBoardListExample> {
|
||||
final AppFlowyBoardController controller = AppFlowyBoardController(
|
||||
onMoveGroup: (fromGroupId, fromIndex, toGroupId, toIndex) {
|
||||
debugPrint('Move item from $fromIndex to $toIndex');
|
||||
},
|
||||
onMoveGroupItem: (groupId, fromIndex, toIndex) {
|
||||
debugPrint('Move $groupId:$fromIndex to $groupId:$toIndex');
|
||||
},
|
||||
onMoveGroupItemToGroup: (fromGroupId, fromIndex, toGroupId, toIndex) {
|
||||
debugPrint('Move $fromGroupId:$fromIndex to $toGroupId:$toIndex');
|
||||
},
|
||||
);
|
||||
|
||||
late AppFlowyBoardScrollController boardController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
boardController = AppFlowyBoardScrollController();
|
||||
final group1 = AppFlowyGroupData(id: "To Do", name: "To Do", items: [
|
||||
TextItem("Card 1"),
|
||||
TextItem("Card 2"),
|
||||
RichTextItem(title: "Card 3", subtitle: 'Aug 1, 2020 4:05 PM'),
|
||||
TextItem("Card 4"),
|
||||
TextItem("Card 5"),
|
||||
TextItem("Card 6"),
|
||||
RichTextItem(title: "Card 7", subtitle: 'Aug 1, 2020 4:05 PM'),
|
||||
RichTextItem(title: "Card 8", subtitle: 'Aug 1, 2020 4:05 PM'),
|
||||
TextItem("Card 9"),
|
||||
]);
|
||||
|
||||
final group2 = AppFlowyGroupData(
|
||||
id: "In Progress",
|
||||
name: "In Progress",
|
||||
items: <AppFlowyGroupItem>[
|
||||
RichTextItem(title: "Card 10", subtitle: 'Aug 1, 2020 4:05 PM'),
|
||||
TextItem("Card 11"),
|
||||
],
|
||||
);
|
||||
|
||||
final group3 = AppFlowyGroupData(
|
||||
id: "Done", name: "Done", items: <AppFlowyGroupItem>[]);
|
||||
|
||||
controller.addGroup(group1);
|
||||
controller.addGroup(group2);
|
||||
controller.addGroup(group3);
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final config = AppFlowyBoardConfig(
|
||||
groupBackgroundColor: HexColor.fromHex('#F7F8FC'),
|
||||
);
|
||||
return AppFlowyBoard(
|
||||
controller: controller,
|
||||
cardBuilder: (context, group, groupItem) {
|
||||
return AppFlowyGroupCard(
|
||||
key: ValueKey(groupItem.id),
|
||||
child: _buildCard(groupItem),
|
||||
);
|
||||
},
|
||||
boardScrollController: boardController,
|
||||
footerBuilder: (context, columnData) {
|
||||
return AppFlowyGroupFooter(
|
||||
icon: const Icon(Icons.add, size: 20),
|
||||
title: const Text('New'),
|
||||
height: 50,
|
||||
margin: config.groupItemPadding,
|
||||
onAddButtonClick: () {
|
||||
boardController.scrollToBottom(columnData.id);
|
||||
},
|
||||
);
|
||||
},
|
||||
headerBuilder: (context, columnData) {
|
||||
return AppFlowyGroupHeader(
|
||||
icon: const Icon(Icons.lightbulb_circle),
|
||||
title: SizedBox(
|
||||
width: 60,
|
||||
child: TextField(
|
||||
controller: TextEditingController()
|
||||
..text = columnData.headerData.groupName,
|
||||
onSubmitted: (val) {
|
||||
controller
|
||||
.getGroupController(columnData.headerData.groupId)!
|
||||
.updateGroupName(val);
|
||||
},
|
||||
),
|
||||
),
|
||||
addIcon: const Icon(Icons.add, size: 20),
|
||||
moreIcon: const Icon(Icons.more_horiz, size: 20),
|
||||
height: 50,
|
||||
margin: config.groupItemPadding,
|
||||
);
|
||||
},
|
||||
groupConstraints: const BoxConstraints.tightFor(width: 240),
|
||||
config: config);
|
||||
}
|
||||
|
||||
Widget _buildCard(AppFlowyGroupItem item) {
|
||||
if (item is TextItem) {
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 30),
|
||||
child: Text(item.s),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (item is RichTextItem) {
|
||||
return RichTextCard(item: item);
|
||||
}
|
||||
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
||||
class RichTextCard extends StatefulWidget {
|
||||
final RichTextItem item;
|
||||
const RichTextCard({
|
||||
required this.item,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<RichTextCard> createState() => _RichTextCardState();
|
||||
}
|
||||
|
||||
class _RichTextCardState extends State<RichTextCard> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
widget.item.title,
|
||||
style: const TextStyle(fontSize: 14),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
widget.item.subtitle,
|
||||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TextItem extends AppFlowyGroupItem {
|
||||
final String s;
|
||||
|
||||
TextItem(this.s);
|
||||
|
||||
@override
|
||||
String get id => s;
|
||||
}
|
||||
|
||||
class RichTextItem extends AppFlowyGroupItem {
|
||||
final String title;
|
||||
final String subtitle;
|
||||
|
||||
RichTextItem({required this.title, required this.subtitle});
|
||||
|
||||
@override
|
||||
String get id => title;
|
||||
}
|
||||
|
||||
extension HexColor on Color {
|
||||
static Color fromHex(String hexString) {
|
||||
final buffer = StringBuffer();
|
||||
if (hexString.length == 6 || hexString.length == 7) buffer.write('ff');
|
||||
buffer.write(hexString.replaceFirst('#', ''));
|
||||
return Color(int.parse(buffer.toString(), radix: 16));
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:appflowy_board/appflowy_board.dart';
|
||||
|
||||
class SingleBoardListExample extends StatefulWidget {
|
||||
const SingleBoardListExample({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<SingleBoardListExample> createState() => _SingleBoardListExampleState();
|
||||
}
|
||||
|
||||
class _SingleBoardListExampleState extends State<SingleBoardListExample> {
|
||||
final AppFlowyBoardController boardData = AppFlowyBoardController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final column = AppFlowyGroupData(
|
||||
id: "1",
|
||||
name: "1",
|
||||
items: [
|
||||
TextItem("a"),
|
||||
TextItem("b"),
|
||||
TextItem("c"),
|
||||
TextItem("d"),
|
||||
],
|
||||
);
|
||||
|
||||
boardData.addGroup(column);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppFlowyBoard(
|
||||
controller: boardData,
|
||||
cardBuilder: (context, column, columnItem) {
|
||||
return _RowWidget(
|
||||
item: columnItem as TextItem, key: ObjectKey(columnItem));
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _RowWidget extends StatelessWidget {
|
||||
final TextItem item;
|
||||
const _RowWidget({Key? key, required this.item}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
key: ObjectKey(item),
|
||||
height: 60,
|
||||
color: Colors.green,
|
||||
child: Center(child: Text(item.s)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TextItem extends AppFlowyGroupItem {
|
||||
final String s;
|
||||
|
||||
TextItem(this.s);
|
||||
|
||||
@override
|
||||
String get id => s;
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
# Project-level configuration.
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(runner LANGUAGES CXX)
|
||||
|
||||
# The name of the executable created for the application. Change this to change
|
||||
# the on-disk name of your application.
|
||||
set(BINARY_NAME "example")
|
||||
# The unique GTK application identifier for this application. See:
|
||||
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
|
||||
set(APPLICATION_ID "com.appflowy.board.example")
|
||||
|
||||
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
||||
# versions of CMake.
|
||||
cmake_policy(SET CMP0063 NEW)
|
||||
|
||||
# Load bundled libraries from the lib/ directory relative to the binary.
|
||||
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
|
||||
|
||||
# Root filesystem for cross-building.
|
||||
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
|
||||
set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
|
||||
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
endif()
|
||||
|
||||
# Define build configuration options.
|
||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
set(CMAKE_BUILD_TYPE "Debug" CACHE
|
||||
STRING "Flutter build mode" FORCE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
|
||||
"Debug" "Profile" "Release")
|
||||
endif()
|
||||
|
||||
# Compilation settings that should be applied to most targets.
|
||||
#
|
||||
# Be cautious about adding new options here, as plugins use this function by
|
||||
# default. In most cases, you should add new options to specific targets instead
|
||||
# of modifying this function.
|
||||
function(APPLY_STANDARD_SETTINGS TARGET)
|
||||
target_compile_features(${TARGET} PUBLIC cxx_std_14)
|
||||
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
|
||||
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
|
||||
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
|
||||
endfunction()
|
||||
|
||||
# Flutter library and tool build rules.
|
||||
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
|
||||
add_subdirectory(${FLUTTER_MANAGED_DIR})
|
||||
|
||||
# System-level dependencies.
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
|
||||
|
||||
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
|
||||
|
||||
# Define the application target. To change its name, change BINARY_NAME above,
|
||||
# not the value here, or `flutter run` will no longer work.
|
||||
#
|
||||
# Any new source files that you add to the application should be added here.
|
||||
add_executable(${BINARY_NAME}
|
||||
"main.cc"
|
||||
"my_application.cc"
|
||||
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
|
||||
)
|
||||
|
||||
# Apply the standard set of build settings. This can be removed for applications
|
||||
# that need different build settings.
|
||||
apply_standard_settings(${BINARY_NAME})
|
||||
|
||||
# Add dependency libraries. Add any application-specific dependencies here.
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
|
||||
|
||||
# Run the Flutter tool portions of the build. This must not be removed.
|
||||
add_dependencies(${BINARY_NAME} flutter_assemble)
|
||||
|
||||
# Only the install-generated bundle's copy of the executable will launch
|
||||
# correctly, since the resources must in the right relative locations. To avoid
|
||||
# people trying to run the unbundled copy, put it in a subdirectory instead of
|
||||
# the default top-level location.
|
||||
set_target_properties(${BINARY_NAME}
|
||||
PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
|
||||
)
|
||||
|
||||
# Generated plugin build rules, which manage building the plugins and adding
|
||||
# them to the application.
|
||||
include(flutter/generated_plugins.cmake)
|
||||
|
||||
|
||||
# === Installation ===
|
||||
# By default, "installing" just makes a relocatable bundle in the build
|
||||
# directory.
|
||||
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
|
||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
|
||||
endif()
|
||||
|
||||
# Start with a clean build bundle directory every time.
|
||||
install(CODE "
|
||||
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
|
||||
" COMPONENT Runtime)
|
||||
|
||||
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
|
||||
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
|
||||
|
||||
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
|
||||
install(FILES "${bundled_library}"
|
||||
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
endforeach(bundled_library)
|
||||
|
||||
# Fully re-copy the assets directory on each build to avoid having stale files
|
||||
# from a previous install.
|
||||
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
|
||||
install(CODE "
|
||||
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
|
||||
" COMPONENT Runtime)
|
||||
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
|
||||
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
|
||||
|
||||
# Install the AOT library on non-Debug builds only.
|
||||
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
endif()
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
// clang-format off
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
// clang-format off
|
||||
|
||||
#ifndef GENERATED_PLUGIN_REGISTRANT_
|
||||
#define GENERATED_PLUGIN_REGISTRANT_
|
||||
|
||||
#include <flutter_linux/flutter_linux.h>
|
||||
|
||||
// Registers Flutter plugins.
|
||||
void fl_register_plugins(FlPluginRegistry* registry);
|
||||
|
||||
#endif // GENERATED_PLUGIN_REGISTRANT_
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
#
|
||||
# Generated file, do not edit.
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
||||
foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
||||
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
|
||||
endforeach(plugin)
|
||||
|
||||
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
|
||||
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
|
||||
endforeach(ffi_plugin)
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
// Application-level settings for the Runner target.
|
||||
//
|
||||
// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
|
||||
// future. If not, the values below would default to using the project name when this becomes a
|
||||
// 'flutter create' template.
|
||||
|
||||
// The application's name. By default this is also the title of the Flutter window.
|
||||
PRODUCT_NAME = example
|
||||
|
||||
// The application's bundle identifier
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.board.example
|
||||
|
||||
// The copyright displayed in application information
|
||||
PRODUCT_COPYRIGHT = Copyright © 2022 com.appflowy.board. All rights reserved.
|
||||
Loaded 100 of 3199 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user