Compare commits

...
74 Commits
Author SHA1 Message Date
Lucas.Xu 764b4db166 chore: bump version 0.2.9 (#3144) 2023-08-10 21:18:31 +08:00
Lucas.Xu 7701533ed2 chore: remove the unused value in supabase_task (#3157) 2023-08-10 21:06:02 +08:00
ferreira-fp 29c89013f0 chore: update PT-BR translations (#3156) 2023-08-10 20:59:53 +08:00
Yijing Huang 1fad5aa804 feat: callout improvement (#3090) 2023-08-10 20:59:24 +08:00
Johan Sutrisno 675e23e971 fix: scroll to heading is too slow (#2952) (#3045) 2023-08-10 18:36:53 +08:00
Lucas.Xu c9ee82ec75 chore: update editor version to 1.2.3 (#3153) 2023-08-10 18:35:27 +08:00
Nathan.fooo 0c9935ccf2 fix: using freeze to refactor the FieldInfo class (#3152) 2023-08-10 15:46:16 +08:00
Lucas.Xu d28846bbaa chore: update German translations 2023-08-10 09:18:18 +07:00
Richard Shiue 58ecf62240 feat: selectOption sort + fix logic in edge cases (#3109) 2023-08-09 12:43:03 +08:00
Nathan.fooo a5eaf15548 refactor: login session (#3149) 2023-08-09 10:13:49 +08:00
fbiere 4589d86d81 inlang: update translations 2023-08-08 18:18:46 +02:00
Richard Shiue 9330df4ce1 fix: overlapped scrollbar in calendar (#3075)
* fix: height for calendar view in document

* fix: hide overlapped scrollbars

* chore: merge remote branch 'upstream/main' into fix-#2618
2023-08-08 16:58:04 +08:00
Richard Shiue 7cdd47757b chore: database tab bar ui improvements (#3093)
* chore: tab bar ui polish

* chore: toolbar buttons ui

* chore: minor padding radius and spacing adjustment

* fix: padding issues

* chore: add view button size and padding

* chore: the carelessness is real

* fix: dark mode colors

* fix: selected icon color
2023-08-08 16:57:25 +08:00
Alex Wallen 9d18e9de40 fix: use dart build script on Windows (#3141) 2023-08-08 16:08:34 +08:00
Lucas.Xu e2dd179001 Merge pull request #3140 from LucasXu0/url_protocol_in_windows_installer 2023-08-08 12:19:56 +07:00
Mathias Mogensen 35a47bfe5d feat: tabs shortcuts (#3112) 2023-08-08 13:09:17 +08:00
Lucas.Xu 9dc209bda7 fix: loading status widget on the signup page flickers. 2023-08-08 11:24:06 +08:00
Lucas.Xu 104851d330 chore: optimize the logout button 2023-08-08 10:58:14 +08:00
Lucas.Xu a1db5bff5c fix: user page inside setting overflow 2023-08-08 10:46:52 +08:00
Lucas.Xu 453346e69c fix: the minHeight of the personal header is too small 2023-08-08 10:32:08 +08:00
Kilu.He 923285bfcf feat: support mention a page (#3117) 2023-08-08 10:07:59 +08:00
Lucas.Xu 0de8b92db6 fix: deeplink doesn't work in windows exe installer 2023-08-08 10:06:29 +08:00
Nathan.fooo 125bcbc9db fix: rebuild application when setting the theme or font (#3139) 2023-08-08 09:08:24 +08:00
Nathan.fooo ef9596f4ab chore: enable single instance desktop application on windows (#3136) 2023-08-07 23:30:14 +08:00
Nathan.fooo 3c04b72932 fix: push to sign in screen when logout (#3127)
* fix: push to sign in screen when logout

* chore: show historical login users

* chore: open historical user

* chore: show historical user

* chore: reload app widget with unique key

* chore: add tooltip for user history
2023-08-07 22:24:04 +08:00
StarAtt a3bea472bf chore: update zh-TW translations (#3131) 2023-08-07 20:59:36 +08:00
Dan Zheng 5ac7b8e165 fix: automatically wrap the date, number or URL cells. 2023-08-07 17:58:24 +08:00
Lucas.Xu 95a938657b feat: support windows supabase deeplink callback (#3133) 2023-08-07 17:54:50 +08:00
Richard Shiue fbb1753350 fix: euro currency number format (#3115) 2023-08-07 17:49:03 +08:00
Lucas.Xu c6401da0b5 feat: support customizing the supabase auth path (#3130) 2023-08-07 14:31:20 +08:00
Lucas.Xu f724185f1c chore: Support CI build universal macOS package (#3124)
* fix: macos release build

* feat: support build arm64 macOS package on CI

* feat: support build arm64 macOS package on CI
2023-08-06 16:06:43 +08:00
Nathan.fooo 63a12a1d2b test: add subscription test (#3126) 2023-08-06 15:54:18 +08:00
Mihir 890f7573f3 fix: favorite selection and context menu doesn't work (#3114) 2023-08-06 12:31:47 +08:00
Nathan.fooo 6f159e741b refactor: remove shared instance KV (#3123)
* refactor: remove shared instance KV

* test: enable document test
2023-08-06 11:51:03 +08:00
Nathan.fooo 9a72f31d60 feat: show loading indicator when loading data from remote (#3120)
* chore: show circle indicator if fetch the data from remote

* chore: fix the lb warnings

* chore: create sdk-build for macOS
2023-08-05 15:02:05 +08:00
Mayur MahajanandMathias Mogensen ab7acbd5de chore: german translation for new property and shortcuts (#3116)
* chore: translation for new property nd shortcuts

* Update frontend/resources/translations/de-DE.json

---------

Co-authored-by: Mathias Mogensen <42929161+Xazin@users.noreply.github.com>
2023-08-04 20:46:09 +02:00
Lucas.Xu b7dfc85208 fix: macos release build (#3118) 2023-08-04 21:22:28 +08:00
Kilu.HeandLucas.Xu 16a01e11ed feat: support updating the view name and icon through document (#3099)
* feat: support updating the view name and icon through document

* fix: store expand pages

* fix: refactor text link

* fix: update cargo.toml

* fix: update test

* fix: update event map

* fix: move deal with icon codes to a single file

* fix: delete useless code from flutter

* fix: document banner

* fix: build error

* fix: update rust library

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2023-08-04 19:27:14 +08:00
Lucas.Xu 70914e6228 chore: support building universal macos package (#3110) 2023-08-03 20:02:44 +08:00
Lucas.Xu 45cbd70bf4 chore: bump version 0.2.8 (#3105) 2023-08-03 15:07:01 +08:00
Lucas.Xu fb9bc359b8 feat: disable moving page into the database (#3107) 2023-08-03 15:06:02 +08:00
Nathan.fooo 135d8a811e chore: fix historical user open issue (#3106) 2023-08-03 12:07:42 +08:00
Nathan.fooo 03518c951c ci: disable document test (#3104) 2023-08-03 10:33:25 +08:00
Nathan.fooo 03b8f2ccb2 feat: Get started doc migration (#3102)
* feat: migrate empty document

* chore: update collab rev

* chore: fmt
2023-08-03 09:14:52 +08:00
Lucas.Xu a40c639a96 fix: dispose the listener when the favorite bloc dispose (#3103) 2023-08-03 08:50:56 +08:00
Nathan.foooandMathias Mogensen ea0c4e96d2 feat: implement magic link login (#3086)
* feat: implement magic link login

* ci: create env file

* ci: generate flutter env files

* ci: disable inject env

* chore: update table name

* Update frontend/appflowy_flutter/lib/env/env.dart

Co-authored-by: Mathias Mogensen <42929161+Xazin@users.noreply.github.com>

* chore: fix compile

---------

Co-authored-by: Mathias Mogensen <42929161+Xazin@users.noreply.github.com>
2023-08-03 08:48:04 +08:00
Mihir a1143e24f3 feat: support favorites folder 2023-08-02 21:20:51 +08:00
Richard Shiue ff79635b2b fix: height for calendar view in document (#3073) 2023-08-02 18:07:44 +08:00
Lucas.Xu f0d2cf7da3 fix: insert reference page in nested page 2023-08-01 21:47:20 +08:00
Richard Shiue 5e2ed56f5b fix: move translation jsons to right folder (#3050) 2023-08-01 14:19:45 +08:00
Mayur Mahajan 70963a55f1 chore: add c/cpp/auto options in code langauges 2023-08-01 14:16:01 +08:00
Lucas.Xu 62be81395d fix: missing c++ library when building iOS (#3092) 2023-08-01 14:15:19 +08:00
Vedant Pandey 7c21f61d2e feat: add duplicate in context menu (#2921) 2023-08-01 11:19:48 +08:00
Lucas.Xu ad4c68a353 chore: update iOS flowy infra UI name (#3091) 2023-08-01 10:07:12 +08:00
Yijing Huang 355061d415 fix: fix theme color (#3089) 2023-07-31 23:01:24 +08:00
Yijing Huang 3039944c74 feat: welcome screen UI improvement (#3057) 2023-07-31 23:01:12 +08:00
Yijing Huang 338e342410 feat: lavender theme refactor 2023-07-31 20:22:56 +08:00
Aman Negi 8c544bc8c4 fix: improve readability in callout 2023-07-31 20:18:43 +08:00
Lucas.Xu 266209caeb feat: implement draggable folder (#3083) 2023-07-31 20:06:01 +08:00
Kilu.He eb77346e5a feat: support add cover and icon in tauri document (#3069)
* feat: support add cover and icon

* feat: emoji picker

* feat: emoji picker
2023-07-31 11:39:44 +08:00
dejvizelo f28c5d849c feat: clear date value (#2700)
* chore: Add _ClearDateFieldButton

* chore: Add new DateCellCalendarEvent to clear date

* chore: Add grid_field_clearDate translation token

* refactor: _ClearDateFieldButton -> _ClearDateButton

* fix: Fix dart(unused_element) in _ClearDateButton

* feat: Handle clearDate

* Revert "feat: Handle clearDate"

This reverts commit fd145ef8e9589f3b4aa1a16e5d72c15b6d0b53c4.

* feat: Handle clearDate

* feat: Add clear_flag field

* feat: check clear_flag to clear date

* chore: update exisitng tests

* fix: add mssing import

* refactor: remove check for timeFormatError

* fix: fix margin on _ClearDateButton

* refactor: _ClearDateButton -> ClearDateButton

* test: add clear date backend unit test

* test: add clear date integration test

* test: add clear date to edit time cell test

* chore: mark ClearDateButton visible for testing

* feat: close date editor popover on clear date

* style: make AppFlowyPopover taller

* fix: trim dateStr in _dateStrFromCellData

* fix: don't dismiss CellEditor after clearDate
2023-07-31 10:23:20 +08:00
Richard ShiueandLucas.Xu ea37f46f88 fix: date field created when reference a calendar (#2910)
* fix: date field created when reference a calendar

* fix: make everything one transaction

* chore: apply suggestions from Lucas

* chore: do the same for board

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2023-07-31 10:12:26 +08:00
Nathan.fooo e68d3f1c71 tests: add supabase tests (#3084) 2023-07-30 08:39:01 +08:00
Jannes Blobel 5741feb7cc Add Transaltion section README.md (#2751)
* Add Transaltion section README.md

AppFlowy has a lot of missing translations. 
I thought you could use your community for the missing translation.

* Fix typo and add backticks for readability in README.md

* Update README.md
2023-07-29 15:00:25 +08:00
Nathan.foooandFu Zi Xiang 2cd88594e8 feat: migrate user data to cloud (#3078)
* refactor: weak passed-in params in handler

* refactor: rename struct

* chore: update tables

* chore: update schema

* chore: add permission

* chore: update tables

* chore: support transaction mode

* chore: workspace database id

* chore: add user workspace

* feat: return list of workspaces

* chore: add user to workspace

* feat: separate database row table

* refactor: update schema

* chore: partition table

* chore: use transaction

* refactor: dir

* refactor: collab db ref

* fix: collab db lock

* chore: rename files

* chore: add tables descriptions

* chore: update readme

* docs: update documentation

* chore: rename crate

* chore: update ref

* chore: update tests

* chore: update tests

* refactor: crate deps

* chore: update crate ref

* chore: remove unused deps

* chore: remove unused deps

* chore: update collab crate refs

* chore: replace client with transaction in pooler

* refactor: return error type

* refactor: use anyhow error in deps

* feat: supabase postgrest user signin (wip)

* fix: Cargo.toml source git deps, changed Error to anyhow::Error

* fix: uuid serialization

* chore: fix conflict

* chore: extend the response

* feat: add implementation place holders

* feat: impl get_user_workspaces

* feat: impl get_user_profile

* test: create workspace

* fix: postgrest: field names and alias

* chore: implement folder restful api

* chore: implement collab storate with restful api

* feat: added placeholders for impl: update_user_profile, check_user

* feat: impl: update_user_profile

* feat: impl: check_user

* fix: use UidResponse, add more debug info for serde serialization error

* fix: get_user_profile: use Optional<UserProfileResponse>

* chore: imple init sync

* chore: support soft delete

* feat: postgresql: add migration test

* feat: postgresql migration test: added UID display and colored output

* feat: postgresql migration test: workspace role

* feat: postgresql migration test: create shared common utils

* feat: postgresql migration test: fixed shebang

* chore: add flush_collab_update pg function

* chore: implement datbaase and document restful api

* chore: migrate to use restful api

* chore: update table schema

* chore: fix tests

* chore: remove unused code

* chore: format code

* chore: remove unused env

* fix: tauri build

* fix: tauri build

---------

Co-authored-by: Fu Zi Xiang <speed2exe@live.com.sg>
2023-07-29 09:46:24 +08:00
Kilu.He a885170869 feat: support editor format text color and bg color (#3061) 2023-07-27 20:40:18 +08:00
Kilu.He 915ce02157 fix: implement the interface of move nested views (#3042)
* fix: implement the interface of move nested views

* fix: update rust ci ubuntu version

* fix: update rust ci version
2023-07-26 16:49:50 +08:00
Lucas.Xu 6e9a5a16a6 fix: windows relesae job (#3038) 2023-07-25 16:41:03 +08:00
Richard Shiue 13c72a31b7 fix: cursor for block options on Windows (#3052) 2023-07-25 16:40:25 +08:00
Richard Shiue 96410b2d32 fix: text overflow when editing a filter 2023-07-25 09:07:21 +07:00
Lucas.Xu d32c2082b6 feat: support building on iOS platform (#3033) 2023-07-21 15:53:57 +08:00
Richard Shiue c368d855c7 feat: enable sort by date + sort fix (#2953) 2023-07-21 15:51:20 +08:00
Mayur Mahajan b1378b4545 feat: customize command shortcuts (#2848) 2023-07-20 16:11:00 +08:00
Kilu.He 5ab64f8835 feat: support views drag and drop (#3004) 2023-07-19 17:59:32 +08:00
654 changed files with 20066 additions and 9499 deletions

No files matched your search

+1
View File
@@ -134,6 +134,7 @@ jobs:
fail_ci_if_error: true
verbose: true
os: ${{ matrix.os }}
token: ${{ secrets.CODECOV_TOKEN }}
attempt_limit: 20
attempt_delay: 10000
+1
View File
@@ -136,5 +136,6 @@ jobs:
fail_ci_if_error: true
verbose: true
os: ${{ matrix.os }}
token: ${{ secrets.CODECOV_TOKEN }}
attempt_limit: 20
attempt_delay: 10000
+99 -3
View File
@@ -84,7 +84,7 @@ jobs:
working-directory: frontend
run: |
flutter config --enable-windows-desktop
cargo make --env APP_VERSION=${{ github.ref_name }} --profile production-windows-x86 appflowy
dart ./scripts/flutter_release_build/build_flowy.dart . ${{ github.ref_name }}
- name: Archive Asset
uses: vimtor/action-zip@v1
@@ -124,7 +124,7 @@ jobs:
asset_name: ${{ env.WINDOWS_INSTALLER_NAME }}.exe
asset_content_type: application/octet-stream
build-for-macOS:
build-for-macOS-x86_64:
name: ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-args }}]
runs-on: ${{ matrix.job.os }}
needs: create-release
@@ -212,6 +212,96 @@ jobs:
asset_name: ${{ env.MACOS_DMG_NAME }}.dmg
asset_content_type: application/octet-stream
build-for-macOS-universal:
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_AARCH64_ZIP_NAME: AppFlowy_${{ github.ref_name }}_macos-universal.zip
MACOS_DMG_NAME: AppFlowy_${{ github.ref_name }}_macos-universal
strategy:
fail-fast: false
matrix:
job:
- {
targets: "aarch64-apple-darwin,x86_64-apple-darwin",
os: macos-11,
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: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
targets: ${{ matrix.job.targets }}
components: rustfmt
- 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
sh scripts/flutter_release_build/build_universal_package_for_macos.sh
- 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_AARCH64_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_AARCH64_ZIP_NAME }}
asset_name: ${{ env.MACOS_AARCH64_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 }}
@@ -360,7 +450,13 @@ jobs:
notify-discord:
runs-on: ubuntu-latest
needs: [build-for-linux, build-for-windows, build-for-macOS]
needs:
[
build-for-linux,
build-for-windows,
build-for-macOS-x86_64,
build-for-macOS-universal,
]
steps:
- name: Notify Discord
run: |
+1 -27
View File
@@ -22,7 +22,6 @@ on:
env:
CARGO_TERM_COLOR: always
RUST_TOOLCHAIN: "1.70"
FLUTTER_VERSION: "3.10.1"
jobs:
test-on-ubuntu:
@@ -40,14 +39,6 @@ jobs:
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: |
@@ -60,11 +51,6 @@ jobs:
workspaces: |
frontend/rust-lib
- name: Build FlowySDK
working-directory: frontend
run: |
cargo make --profile development-linux-x86_64 appflowy-core-dev
- name: Run rust-lib tests
working-directory: frontend/rust-lib
run: RUST_LOG=info RUST_BACKTRACE=1 cargo test --no-default-features --features="rev-sqlite"
@@ -75,16 +61,4 @@ jobs:
- name: clippy rust-lib
run: cargo clippy --features="rev-sqlite"
working-directory: frontend/rust-lib
- 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
working-directory: frontend/rust-lib
+37 -2
View File
@@ -1,11 +1,46 @@
# Release Notes
## Version 0.2.9 - 08/08/2023
### New Features
- Improve tab and shortcut, click with alt/option to open a page in new tab.
- Improve database tab bar UI.
### Bug fixes
- Add button and more action button of the favorite section doesn't work.
- Fix euro currency number format.
- Some UI issues
## Version 0.2.8 - 08/03/2023
### New Features
- Nestable personal folder that supports drag and drop
- Support for favorite folders.
- Support for sorting by date in Grid view.
- Add a duplicate button in the Board context menu.
### Bug fixes
- Improve readability in Callout
- Some UI issues
## Version 0.2.7 - 07/18/2023
### New Features
- Dynamic load themes
- Inline math equation
<img width="1147" src="https://github.com/AppFlowy-IO/AppFlowy/assets/11863087/ac464740-c685-4a85-ae99-1074c1c607e5">
- Open page in new tab
- Create toggle lists to keep things tidy in your pages
- Alt/Option + click to add a text block above
### Bug fixes
- Pasting into a Grid property crashed on Windows
- Double-click a link to open
## Version 0.2.6 - 07/11/2023
+8
View File
@@ -68,6 +68,14 @@ Contributions make the open-source community a fantastic place to learn, inspire
If your Pull Request is accepted as it fixes a bug, adds functionality, or makes AppFlowy's codebase significantly easier to use or understand, **Congratulations!** If your administrative and managerial work behind the scenes sustains the community, **Congratulations!** You are now an official contributor to AppFlowy. Get in touch with us ([link](https://tally.so/r/mKP5z3)) to receive the very special Contributor T-shirt!
Proudly wear your T-shirt and show it to us by tagging [@appflowy](https://twitter.com/appflowy) on Twitter.
## Translations 🌎🗺
[![translation badge](https://inlang.com/badge?url=github.com/AppFlowy-IO/AppFlowy)](https://inlang.com/editor/github.com/AppFlowy-IO/AppFlowy?ref=badge)
To add translations, you can manually edit the JSON translation files in `/frontend/appflowy_flutter/assets/translations`, use the [inlang online editor](https://inlang.com/editor/github.com/AppFlowy-IO/AppFlowy), or run `npx inlang machine translate` to add missing translations.
## Join the community to build AppFlowy together
<a href="https://github.com/AppFlowy-IO/AppFlowy/graphs/contributors">
+22 -45
View File
@@ -43,47 +43,34 @@
},
"cwd": "${workspaceRoot}/appflowy_flutter"
},
{
"name": "AF-iOS: Clean + Rebuild All",
"request": "launch",
"program": "./lib/main.dart",
"type": "dart",
"preLaunchTask": "AF: Clean + Rebuild All (iOS)",
"env": {
"RUST_LOG": "trace"
},
"cwd": "${workspaceRoot}/appflowy_flutter"
},
{
"name": "AF-iOS-Simulator: Clean + Rebuild All",
"request": "launch",
"program": "./lib/main.dart",
"type": "dart",
"preLaunchTask": "AF: Clean + Rebuild All (iOS Simulator)",
"env": {
"RUST_LOG": "trace"
},
"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-android: Clean + Rebuild All",
"request": "launch",
"program": "./lib/main.dart",
"type": "dart",
"preLaunchTask": "AF: Clean + Rebuild All (Android)",
"env": {
"RUST_LOG": "info"
},
"cwd": "${workspaceRoot}/appflowy_flutter"
},
{
// https://tauri.app/v1/guides/debugging/vs-code
"type": "lldb",
@@ -99,15 +86,5 @@
"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/"
// },
]
}
+31 -4
View File
@@ -28,13 +28,13 @@
}
},
{
"label": "AF: Clean + Rebuild All (Android)",
"label": "AF: Clean + Rebuild All (iOS)",
"type": "shell",
"dependsOrder": "sequence",
"dependsOn": [
"AF: Dart Clean",
"AF: Flutter Clean",
"AF: Build Appflowy Core_for_android",
"AF: Build Appflowy Core For iOS",
"AF: Flutter Pub Get",
"AF: Flutter Package Get",
"AF: Generate Language Files",
@@ -46,9 +46,36 @@
}
},
{
"label": "AF: Build Appflowy Core_for_android",
"label": "AF: Clean + Rebuild All (iOS Simulator)",
"type": "shell",
"command": "cargo make --profile development-android appflowy-core-dev-android",
"dependsOrder": "sequence",
"dependsOn": [
"AF: Dart Clean",
"AF: Flutter Clean",
"AF: Build Appflowy Core For iOS Simulator",
"AF: Flutter Pub Get",
"AF: Flutter Package Get",
"AF: Generate Language Files",
"AF: Generate Freezed Files"
],
"presentation": {
"reveal": "always",
"panel": "new"
}
},
{
"label": "AF: Build Appflowy Core For iOS",
"type": "shell",
"command": "cargo make --profile development-ios-arm64 appflowy-core-dev-ios",
"group": "build",
"options": {
"cwd": "${workspaceFolder}"
}
},
{
"label": "AF: Build Appflowy Core For iOS Simulator",
"type": "shell",
"command": "cargo make --profile development-ios-arm64-sim appflowy-core-dev-ios",
"group": "build",
"options": {
"cwd": "${workspaceFolder}"
+30 -7
View File
@@ -2,6 +2,7 @@
extend = [
{ path = "scripts/makefile/desktop.toml" },
{ path = "scripts/makefile/mobile.toml" },
{ path = "scripts/makefile/protobuf.toml" },
{ path = "scripts/makefile/tests.toml" },
{ path = "scripts/makefile/docker.toml" },
@@ -23,7 +24,7 @@ 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.2.7"
CURRENT_APP_VERSION = "0.2.9"
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite"
PRODUCT_NAME = "AppFlowy"
# CRATE_TYPE: https://doc.rust-lang.org/reference/linkage.html
@@ -59,6 +60,7 @@ BUILD_FLAG = "debug"
FLUTTER_OUTPUT_DIR = "Debug"
PRODUCT_EXT = "app"
BUILD_ARCHS = "arm64"
BUILD_ACTIVE_ARCHS_ONLY = true
CRATE_TYPE = "staticlib"
[env.development-mac-x86_64]
@@ -69,6 +71,7 @@ BUILD_FLAG = "debug"
FLUTTER_OUTPUT_DIR = "Debug"
PRODUCT_EXT = "app"
BUILD_ARCHS = "x86_64"
BUILD_ACTIVE_ARCHS_ONLY = true
CRATE_TYPE = "staticlib"
[env.production-mac-arm64]
@@ -79,6 +82,7 @@ FLUTTER_OUTPUT_DIR = "Release"
PRODUCT_EXT = "app"
APP_ENVIRONMENT = "production"
BUILD_ARCHS = "arm64"
BUILD_ACTIVE_ARCHS_ONLY = false
CRATE_TYPE = "staticlib"
[env.production-mac-x86_64]
@@ -89,8 +93,17 @@ FLUTTER_OUTPUT_DIR = "Release"
PRODUCT_EXT = "app"
APP_ENVIRONMENT = "production"
BUILD_ARCHS = "x86_64"
BUILD_ACTIVE_ARCHS_ONLY = false
CRATE_TYPE = "staticlib"
[env.production-mac-universal]
BUILD_FLAG = "release"
TARGET_OS = "macos"
FLUTTER_OUTPUT_DIR = "Release"
PRODUCT_EXT = "app"
BUILD_ACTIVE_ARCHS_ONLY = false
APP_ENVIRONMENT = "production"
[env.development-windows-x86]
TARGET_OS = "windows"
RUST_COMPILE_TARGET = "x86_64-pc-windows-msvc"
@@ -150,6 +163,22 @@ LINUX_ARCH = "arm64"
APP_ENVIRONMENT = "production"
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite,openssl_vendored"
[env.development-ios-arm64-sim]
BUILD_FLAG = "debug"
TARGET_OS = "ios"
FLUTTER_OUTPUT_DIR = "Debug"
RUST_COMPILE_TARGET = "aarch64-apple-ios-sim"
BUILD_ARCHS = "arm64"
CRATE_TYPE = "staticlib"
[env.development-ios-arm64]
BUILD_FLAG = "debug"
TARGET_OS = "ios"
FLUTTER_OUTPUT_DIR = "Debug"
RUST_COMPILE_TARGET = "aarch64-apple-ios"
BUILD_ARCHS = "arm64"
CRATE_TYPE = "staticlib"
[tasks.echo_env]
script = ['''
echo "-------- Env Parameters --------"
@@ -165,12 +194,6 @@ script = ['''
''']
script_runner = "@shell"
[env.production-ios]
BUILD_FLAG = "release"
TARGET_OS = "ios"
FLUTTER_OUTPUT_DIR = "Release"
PRODUCT_EXT = "ipa"
[env.development-android]
BUILD_FLAG = "debug"
TARGET_OS = "android"
@@ -1,3 +0,0 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 6L18.781 11.9243L25 12.8801L20.5 17.489L21.562 24L16 20.9243L10.438 24L11.5 17.489L7 12.8801L13.219 11.9243L16 6Z" fill="#FFD667" stroke="#FFD667" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 329 B

@@ -1,3 +0,0 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 6L18.781 11.9243L25 12.8801L20.5 17.489L21.562 24L16 20.9243L10.438 24L11.5 17.489L7 12.8801L13.219 11.9243L16 6Z" stroke="#333333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 314 B

@@ -1,3 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 4.5L14.0858 8.94322L18.75 9.66009L15.375 13.1167L16.1715 18L12 15.6932L7.8285 18L8.625 13.1167L5.25 9.66009L9.91425 8.94322L12 4.5Z" stroke="#333333" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 3L9.3905 5.96215L12.5 6.44006L10.25 8.74448L10.781 12L8 10.4621L5.219 12L5.75 8.74448L3.5 6.44006L6.6095 5.96215L8 3Z" fill="#FFD667" stroke="#FFD667" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 334 B

After

Width:  |  Height:  |  Size: 315 B

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 3L9.3905 5.96215L12.5 6.44006L10.25 8.74448L10.781 12L8 10.4621L5.219 12L5.75 8.74448L3.5 6.44006L6.6095 5.96215L8 3Z" fill="#none" stroke="#FFFFFF" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 313 B

@@ -0,0 +1,50 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database_view/widgets/card/card.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import '../util/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('board row test', () {
testWidgets('delete item in ToDo card', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Board);
const name = 'Card 1';
final card1 = find.findTextInFlowyText(name);
await tester.hoverOnWidget(
card1,
onHover: () async {
final moreOption = find.byType(CardMoreOption);
await tester.tapButton(moreOption);
},
);
await tester.tapButtonWithName(LocaleKeys.button_delete.tr());
expect(find.findTextInFlowyText(name), findsNothing);
});
testWidgets('duplicate item in ToDo card', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Board);
const name = 'Card 1';
final card1 = find.findTextInFlowyText(name);
await tester.hoverOnWidget(
card1,
onHover: () async {
final moreOption = find.byType(CardMoreOption);
await tester.tapButton(moreOption);
},
);
await tester.tapButtonWithName(LocaleKeys.button_duplicate.tr());
expect(find.textContaining(name, findRichText: true), findsNWidgets(2));
});
});
}
@@ -0,0 +1,10 @@
import 'package:integration_test/integration_test.dart';
import 'board_row_test.dart' as board_row_test;
void startTesting() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
// Board integration tests
board_row_test.main();
}
@@ -14,8 +14,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateCalendarButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Calendar);
// open setting
await tester.tapDatabaseSettingButton();
@@ -36,7 +35,11 @@ void main() {
await tester.tapGoButton();
// Create calendar view
await tester.createNewPageWithName(ViewLayoutPB.Calendar, 'calendar');
const name = 'calendar';
await tester.createNewPageWithName(
name: name,
layout: ViewLayoutPB.Calendar,
);
// Open setting
await tester.tapDatabaseSettingButton();
@@ -47,9 +50,9 @@ void main() {
await tester.tapFirstDayOfWeekStartFromMonday();
// Open the other page and open the new calendar page again
await tester.openPage(readme);
await tester.openPage(gettingStarted);
await tester.pumpAndSettle(const Duration(milliseconds: 300));
await tester.openPage('calendar');
await tester.openPage(name, layout: ViewLayoutPB.Calendar);
// Open setting again and check the start from Monday is selected
await tester.tapDatabaseSettingButton();
@@ -65,8 +68,7 @@ void main() {
await tester.tapGoButton();
// Create the calendar view
await tester.tapAddButton();
await tester.tapCreateCalendarButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Calendar);
// Scroll until today's date cell is visible
await tester.scrollToToday();
@@ -135,8 +137,7 @@ void main() {
await tester.tapGoButton();
// Create the calendar view
await tester.tapAddButton();
await tester.tapCreateCalendarButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Calendar);
// Create a new event on the first of this month
final today = DateTime.now();
@@ -15,8 +15,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
await tester.editCell(
rowIndex: 0,
@@ -38,7 +37,10 @@ void main() {
testWidgets('edit multiple text cells', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.createNewPageWithName(ViewLayoutPB.Grid, 'my grid');
await tester.createNewPageWithName(
name: 'my grid',
layout: ViewLayoutPB.Grid,
);
await tester.createField(FieldType.RichText, 'description');
await tester.editCell(
@@ -75,8 +77,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
const fieldType = FieldType.Number;
@@ -134,8 +135,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
await tester.assertCheckboxCell(rowIndex: 0, isSelected: false);
await tester.tapCheckboxCellInGrid(rowIndex: 0);
@@ -153,8 +153,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
const fieldType = FieldType.CreatedTime;
// Create a create time field
@@ -172,8 +171,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
const fieldType = FieldType.LastEditedTime;
// Create a last time field
@@ -191,8 +189,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
const fieldType = FieldType.DateTime;
await tester.createField(fieldType, fieldType.name);
@@ -268,6 +265,18 @@ void main() {
content: DateFormat('dd/MM/y hh:mm a').format(now),
);
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
await tester.findDateEditor(findsOneWidget);
// Clear the date and time
await tester.clearDate();
await tester.assertDateCellInGrid(
rowIndex: 0,
fieldType: fieldType,
content: '',
);
await tester.pumpAndSettle();
});
@@ -276,9 +285,9 @@ void main() {
await tester.tapGoButton();
const fieldType = FieldType.SingleSelect;
await tester.tapAddButton();
// When create a grid, it will create a single select field by default
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// Tap the cell to invoke the selection option editor
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
@@ -354,8 +363,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
const fieldType = FieldType.MultiSelect;
await tester.createField(fieldType, fieldType.name);
@@ -17,8 +17,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// Invoke the field editor
await tester.tapGridFieldWithName('Name');
@@ -35,8 +34,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// Invoke the field editor
await tester.tapGridFieldWithName('Type');
@@ -58,8 +56,7 @@ void main() {
await tester.tapGoButton();
// create a new grid
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// create a field
await tester.createField(FieldType.Checklist, 'checklist');
@@ -73,8 +70,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// create a field
await tester.createField(FieldType.Checkbox, 'New field 1');
@@ -94,8 +90,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// create a field
await tester.scrollToRight(find.byType(GridPage));
@@ -115,8 +110,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// create a field
await tester.scrollToRight(find.byType(GridPage));
@@ -136,8 +130,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
await tester.scrollToRight(find.byType(GridPage));
await tester.tapNewPropertyButton();
@@ -157,8 +150,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
for (final fieldType in [
FieldType.Checklist,
@@ -190,7 +182,9 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.createNewPageWithName(ViewLayoutPB.Grid);
await tester.createNewPageWithName(
layout: ViewLayoutPB.Grid,
);
// Invoke the field editor
await tester.tapGridFieldWithName('Type');
@@ -9,7 +9,7 @@ import 'util/database_test_op.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('grid', () {
group('database filter', () {
testWidgets('add text filter', (tester) async {
await tester.openV020database();
@@ -1,5 +1,6 @@
import 'package:appflowy/plugins/database_view/widgets/row/row_banner.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flutter/material.dart';
@@ -19,8 +20,7 @@ void main() {
await tester.tapGoButton();
// Create a new grid
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page
await tester.openFirstRowDetailPage();
@@ -34,8 +34,7 @@ void main() {
await tester.tapGoButton();
// Create a new grid
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page
await tester.openFirstRowDetailPage();
@@ -55,8 +54,7 @@ void main() {
await tester.tapGoButton();
// Create a new grid
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page
await tester.openFirstRowDetailPage();
@@ -85,8 +83,7 @@ void main() {
await tester.tapGoButton();
// Create a new grid
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page
await tester.openFirstRowDetailPage();
@@ -108,8 +105,7 @@ void main() {
await tester.tapGoButton();
// Create a new grid
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page
await tester.openFirstRowDetailPage();
@@ -144,8 +140,7 @@ void main() {
await tester.tapGoButton();
// Create a new grid
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page
await tester.openFirstRowDetailPage();
@@ -160,8 +155,7 @@ void main() {
await tester.tapGoButton();
// Create a new grid
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page
await tester.openFirstRowDetailPage();
@@ -201,8 +195,7 @@ void main() {
await tester.tapGoButton();
// Create a new grid
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page
await tester.openFirstRowDetailPage();
@@ -241,8 +234,7 @@ void main() {
await tester.tapGoButton();
// Create a new grid
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page
await tester.openFirstRowDetailPage();
@@ -258,8 +250,7 @@ void main() {
await tester.tapGoButton();
// Create a new grid
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page
await tester.openFirstRowDetailPage();
@@ -1,3 +1,4 @@
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
@@ -12,8 +13,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
await tester.tapCreateRowButtonInGrid();
// The initial number of rows is 3
@@ -25,8 +25,8 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
await tester.hoverOnFirstRowOfGrid();
await tester.tapCreateRowButtonInRowMenuOfGrid();
@@ -41,8 +41,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
await tester.hoverOnFirstRowOfGrid();
// Open the row menu and then click the delete
@@ -60,8 +59,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
await tester.assertRowCountInGridPage(3);
await tester.pumpAndSettle();
@@ -1,4 +1,5 @@
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
@@ -13,8 +14,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// open setting
await tester.tapDatabaseSettingButton();
@@ -31,8 +31,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// open setting
await tester.tapDatabaseSettingButton();
@@ -10,22 +10,22 @@ void main() {
group('grid', () {
testWidgets('add text sort', (tester) async {
await tester.openV020database();
// create a filter
// create a sort
await tester.tapDatabaseSortButton();
await tester.tapCreateSortByFieldType(FieldType.RichText, 'Name');
// check the text cell order
final textCells = <String>[
'',
'',
'',
'',
'',
'A',
'B',
'C',
'D',
'E',
'',
'',
'',
'',
'',
];
for (final (index, content) in textCells.indexed) {
await tester.assertCellContent(
@@ -142,7 +142,6 @@ void main() {
// check the number cell order
for (final (index, content) in <String>[
'',
'-2',
'-1',
'0.1',
@@ -152,6 +151,7 @@ void main() {
'10',
'11',
'12',
'',
].indexed) {
await tester.assertCellContent(
rowIndex: index,
@@ -186,53 +186,74 @@ void main() {
await tester.pumpAndSettle();
});
testWidgets('add number and text sort', (tester) async {
testWidgets('add checkbox and number sort', (tester) async {
await tester.openV020database();
// create a filter
await tester.tapDatabaseSortButton();
await tester.tapCreateSortByFieldType(FieldType.Number, 'number');
await tester.tapCreateSortByFieldType(FieldType.Checkbox, 'Done');
// open the sort menu and select number order by descending
// open the sort menu and sort checkbox by descending
await tester.tapSortMenuInSettingBar();
await tester.tapSortButtonByName('number');
await tester.tapSortButtonByName('Done');
await tester.tapSortByDescending();
for (final (index, content) in <String>[
'12',
'11',
'10',
'2',
'1',
'0.2',
'0.1',
'-1',
'-2',
'',
for (final (index, content) in <bool>[
true,
true,
true,
true,
true,
false,
false,
false,
false,
false,
].indexed) {
await tester.assertCellContent(
await tester.assertCheckboxCell(
rowIndex: index,
fieldType: FieldType.Number,
content: content,
isSelected: content,
);
}
// add another sort, this time by number descending
await tester.tapSortMenuInSettingBar();
await tester.tapCreateSortByFieldTypeInSortMenu(
FieldType.RichText,
'Name',
FieldType.Number,
'number',
);
await tester.tapSortButtonByName('number');
await tester.tapSortByDescending();
// check checkbox cell order
for (final (index, content) in <bool>[
true,
true,
true,
true,
true,
false,
false,
false,
false,
false,
].indexed) {
await tester.assertCheckboxCell(
rowIndex: index,
isSelected: content,
);
}
// check number cell order
for (final (index, content) in <String>[
'1',
'0.2',
'0.1',
'-1',
'-2',
'12',
'11',
'10',
'2',
'',
'-1',
'-2',
'0.1',
'0.2',
'1',
].indexed) {
await tester.assertCellContent(
rowIndex: index,
@@ -241,26 +262,6 @@ void main() {
);
}
// check text cell order
for (final (index, content) in <String>[
'',
'',
'',
'',
'',
'A',
'B',
'C',
'D',
'E',
].indexed) {
await tester.assertCellContent(
rowIndex: index,
fieldType: FieldType.RichText,
content: content,
);
}
await tester.pumpAndSettle();
});
});
@@ -15,8 +15,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// Create board view
await tester.tapCreateLinkedDatabaseViewButton(AddButtonAction.board);
@@ -37,8 +36,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// Create board view
await tester.tapCreateLinkedDatabaseViewButton(AddButtonAction.board);
@@ -63,8 +61,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAddButton();
await tester.tapCreateGridButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
// Create board view
await tester.tapCreateLinkedDatabaseViewButton(AddButtonAction.board);
@@ -0,0 +1,47 @@
import 'dart:io';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import '../util/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('copy and paste in document', () {
testWidgets('paste multiple lines at the first line', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
// create a new document
await tester.createNewPageWithName();
// mock the clipboard
const lines = 3;
AppFlowyClipboard.mockSetData(
AppFlowyClipboardData(
text: List.generate(lines, (index) => 'line $index').join('\n'),
),
);
// paste the text
await tester.simulateKeyEvent(
LogicalKeyboardKey.keyV,
isControlPressed: Platform.isLinux || Platform.isWindows,
isMetaPressed: Platform.isMacOS,
);
await tester.pumpAndSettle();
final editorState = tester.editor.getCurrentEditorState();
expect(editorState.document.root.children.length, 4);
for (var i = 0; i < lines; i++) {
expect(
editorState.getNodeAtPath([i])!.delta!.toPlainText(),
'line $i',
);
}
});
});
}
@@ -17,9 +17,7 @@ void main() {
await tester.tapGoButton();
// create a new document
await tester.tapAddButton();
await tester.tapCreateDocumentButton();
await tester.pumpAndSettle();
await tester.createNewPageWithName();
// expect to see a new document
tester.expectToSeePageName(
@@ -35,19 +33,21 @@ void main() {
await tester.tapGoButton();
// delete the readme page
await tester.hoverOnPageName(readme);
await tester.tapDeletePageButton();
await tester.hoverOnPageName(
gettingStarted,
onHover: () async => await tester.tapDeletePageButton(),
);
// the banner should show up and the readme page should be gone
tester.expectToSeeDocumentBanner();
tester.expectNotToSeePageName(readme);
tester.expectNotToSeePageName(gettingStarted);
// restore the readme page
await tester.tapRestoreButton();
// the banner should be gone and the readme page should be back
tester.expectNotToSeeDocumentBanner();
tester.expectToSeePageName(readme);
tester.expectToSeePageName(gettingStarted);
});
testWidgets('delete the readme page and delete it permanently',
@@ -57,19 +57,21 @@ void main() {
await tester.tapGoButton();
// delete the readme page
await tester.hoverOnPageName(readme);
await tester.tapDeletePageButton();
await tester.hoverOnPageName(
gettingStarted,
onHover: () async => await tester.tapDeletePageButton(),
);
// the banner should show up and the readme page should be gone
tester.expectToSeeDocumentBanner();
tester.expectNotToSeePageName(readme);
tester.expectNotToSeePageName(gettingStarted);
// delete the page permanently
await tester.tapDeletePermanentlyButton();
// the banner should be gone and the readme page should be gone
tester.expectNotToSeeDocumentBanner();
tester.expectNotToSeePageName(readme);
tester.expectNotToSeePageName(gettingStarted);
});
});
}
@@ -10,6 +10,7 @@ import 'document_with_inline_page_test.dart' as document_with_inline_page_test;
import 'document_with_toggle_list_test.dart' as document_with_toggle_list_test;
import 'edit_document_test.dart' as document_edit_test;
import 'document_with_outline_block_test.dart' as document_with_outline_block;
import 'document_copy_and_paste_test.dart' as document_copy_and_paste_test;
void startTesting() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
@@ -23,4 +24,5 @@ void startTesting() {
document_with_cover_image_test.main();
document_with_outline_block.main();
document_with_toggle_list_test.main();
document_copy_and_paste_test.main();
}
@@ -1,9 +1,12 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database_view/board/presentation/board_page.dart';
import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_page.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/link_to_page_widget.dart';
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/uuid.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
@@ -61,6 +64,54 @@ void main() {
findsOneWidget,
);
});
testWidgets('create a grid inside a document', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await createInlineDatabase(tester, ViewLayoutPB.Grid);
// validate the referenced grid is inserted
expect(
find.descendant(
of: find.byType(AppFlowyEditor),
matching: find.byType(GridPage),
),
findsOneWidget,
);
});
testWidgets('create a board inside a document', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await createInlineDatabase(tester, ViewLayoutPB.Board);
// validate the referenced grid is inserted
expect(
find.descendant(
of: find.byType(AppFlowyEditor),
matching: find.byType(BoardPage),
),
findsOneWidget,
);
});
testWidgets('create a calendar inside a document', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await createInlineDatabase(tester, ViewLayoutPB.Calendar);
// validate the referenced grid is inserted
expect(
find.descendant(
of: find.byType(AppFlowyEditor),
matching: find.byType(CalendarPage),
),
findsOneWidget,
);
});
});
}
@@ -73,13 +124,15 @@ Future<void> insertReferenceDatabase(
final id = uuid();
final name = '${layout.name}_$id';
await tester.createNewPageWithName(
layout,
name,
name: name,
layout: layout,
openAfterCreated: false,
);
// create a new document
await tester.createNewPageWithName(
ViewLayoutPB.Document,
'insert_a_reference_${layout.name}',
name: 'insert_a_reference_${layout.name}',
layout: ViewLayoutPB.Document,
openAfterCreated: true,
);
// tap the first line of the document
await tester.editor.tapLineOfEditorAt(0);
@@ -98,3 +151,38 @@ Future<void> insertReferenceDatabase(
expect(referencedDatabase, findsOneWidget);
await tester.tapButton(referencedDatabase);
}
Future<void> createInlineDatabase(
WidgetTester tester,
ViewLayoutPB layout,
) async {
// create a new document
final documentName = 'insert_a_inline_${layout.name}';
await tester.createNewPageWithName(
name: documentName,
layout: ViewLayoutPB.Document,
openAfterCreated: true,
);
// tap the first line of the document
await tester.editor.tapLineOfEditorAt(0);
// insert a referenced view
await tester.editor.showSlashMenu();
final name = switch (layout) {
ViewLayoutPB.Grid => LocaleKeys.document_slashMenu_grid_createANewGrid.tr(),
ViewLayoutPB.Board =>
LocaleKeys.document_slashMenu_board_createANewBoard.tr(),
ViewLayoutPB.Calendar =>
LocaleKeys.document_slashMenu_calendar_createANewCalendar.tr(),
_ => '',
};
await tester.editor.tapSlashMenuItemWithName(
name,
);
await tester.pumpAndSettle();
final childViews = tester
.widget<SingleInnerViewItem>(tester.findPageName(documentName))
.view
.childViews;
expect(childViews.length, 1);
}
@@ -21,8 +21,8 @@ void main() {
// create a new document
await tester.createNewPageWithName(
ViewLayoutPB.Document,
LocaleKeys.document_plugins_createInlineMathEquation.tr(),
name: LocaleKeys.document_plugins_createInlineMathEquation.tr(),
layout: ViewLayoutPB.Document,
);
// tap the first line of the document
@@ -67,8 +67,8 @@ void main() {
// create a new document
await tester.createNewPageWithName(
ViewLayoutPB.Document,
LocaleKeys.document_plugins_createInlineMathEquation.tr(),
name: LocaleKeys.document_plugins_createInlineMathEquation.tr(),
layout: ViewLayoutPB.Document,
);
// tap the first line of the document
@@ -62,9 +62,11 @@ void main() {
final pageName = await insertingInlinePage(tester, ViewLayoutPB.Document);
// rename
await tester.hoverOnPageName(pageName);
const newName = 'RenameToNewPageName';
await tester.renamePage(newName);
await tester.hoverOnPageName(
pageName,
onHover: () async => await tester.renamePage(newName),
);
final finder = find.descendant(
of: find.byType(MentionPageBlock),
matching: find.findTextInFlowyText(newName),
@@ -79,8 +81,11 @@ void main() {
final pageName = await insertingInlinePage(tester, ViewLayoutPB.Grid);
// rename
await tester.hoverOnPageName(pageName);
await tester.tapDeletePageButton();
await tester.hoverOnPageName(
pageName,
layout: ViewLayoutPB.Grid,
onHover: () async => await tester.tapDeletePageButton(),
);
final finder = find.descendant(
of: find.byType(MentionPageBlock),
matching: find.findTextInFlowyText(pageName),
@@ -101,13 +106,14 @@ Future<String> insertingInlinePage(
final id = uuid();
final name = '${layout.name}_$id';
await tester.createNewPageWithName(
layout,
name,
name: name,
layout: layout,
openAfterCreated: false,
);
// create a new document
await tester.createNewPageWithName(
ViewLayoutPB.Document,
'insert_a_inline_page_${layout.name}',
name: 'insert_a_inline_page_${layout.name}',
layout: ViewLayoutPB.Document,
);
// tap the first line of the document
await tester.editor.tapLineOfEditorAt(0);
@@ -25,7 +25,7 @@ void main() {
// create a new document
await tester.createNewPageWithName(
ViewLayoutPB.Document,
layout: ViewLayoutPB.Document,
);
// tap the first line of the document
@@ -16,8 +16,8 @@ void main() {
await tester.tapGoButton();
await tester.createNewPageWithName(
ViewLayoutPB.Document,
'outline_test',
name: 'outline_test',
layout: ViewLayoutPB.Document,
);
await tester.editor.tapLineOfEditorAt(0);
@@ -33,8 +33,8 @@ void main() {
await tester.tapGoButton();
await tester.createNewPageWithName(
ViewLayoutPB.Document,
'outline_test',
name: 'outline_test',
layout: ViewLayoutPB.Document,
);
await tester.editor.tapLineOfEditorAt(0);
@@ -32,7 +32,7 @@ void main() {
// create a new document
await tester.createNewPageWithName(
ViewLayoutPB.Document,
layout: ViewLayoutPB.Document,
);
// tap the first line of the document
@@ -78,7 +78,7 @@ void main() {
// create a new document
await tester.createNewPageWithName(
ViewLayoutPB.Document,
layout: ViewLayoutPB.Document,
);
// tap the first line of the document
@@ -118,7 +118,7 @@ void main() {
// create a new document
await tester.createNewPageWithName(
ViewLayoutPB.Document,
layout: ViewLayoutPB.Document,
);
// tap the first line of the document
@@ -156,7 +156,7 @@ void main() {
// create a new document
await tester.createNewPageWithName(
ViewLayoutPB.Document,
layout: ViewLayoutPB.Document,
);
// tap the first line of the document
@@ -191,7 +191,7 @@ void main() {
// create a new document
await tester.createNewPageWithName(
ViewLayoutPB.Document,
layout: ViewLayoutPB.Document,
);
// tap the first line of the document
@@ -18,7 +18,10 @@ void main() {
// create a new document called Sample
const pageName = 'Sample';
await tester.createNewPageWithName(ViewLayoutPB.Document, pageName);
await tester.createNewPageWithName(
name: pageName,
layout: ViewLayoutPB.Document,
);
// focus on the editor
await tester.editor.tapLineOfEditorAt(0);
@@ -56,7 +59,7 @@ void main() {
);
// switch to other page and switch back
await tester.openPage(readme);
await tester.openPage(gettingStarted);
await tester.openPage(pageName);
// the numbered list should be kept
@@ -72,7 +75,10 @@ void main() {
// create a new document called Sample
const pageName = 'Sample';
await tester.createNewPageWithName(ViewLayoutPB.Document, pageName);
await tester.createNewPageWithName(
name: pageName,
layout: ViewLayoutPB.Document,
);
// focus on the editor
await tester.editor.tapLineOfEditorAt(0);
@@ -85,7 +91,7 @@ void main() {
}
// switch to other page and switch back
await tester.openPage(readme);
await tester.openPage(gettingStarted);
await tester.openPage(pageName);
// this screenshots are different on different platform, so comment it out temporarily.
@@ -16,10 +16,10 @@ void main() {
final context = await tester.initializeAppFlowy();
await tester.tapGoButton();
// expect to see a readme page
tester.expectToSeePageName(readme);
// expect to see a getting started page
tester.expectToSeePageName(gettingStarted);
await tester.tapAddButton();
await tester.tapAddViewButton();
await tester.tapImportButton();
final testFileNames = ['test1.md', 'test2.md'];
@@ -14,6 +14,9 @@ import 'document/document_test_runner.dart' as document_test_runner;
import 'import_files_test.dart' as import_files_test;
import 'share_markdown_test.dart' as share_markdown_test;
import 'switch_folder_test.dart' as switch_folder_test;
import 'sidebar/sidebar_test_runner.dart' as sidebar_test_runner;
import 'board/board_test_runner.dart' as board_test_runner;
import 'tabs_test.dart' as tabs_test;
/// The main task runner for all integration tests in AppFlowy.
///
@@ -31,6 +34,12 @@ void main() {
// Document integration tests
document_test_runner.startTesting();
// Sidebar integration tests
sidebar_test_runner.startTesting();
// Board integration test
board_test_runner.startTesting();
// Database integration tests
database_cell_test.main();
database_field_test.main();
@@ -43,6 +52,9 @@ void main() {
database_view_test.main();
database_calendar_test.main();
// Tabs
tabs_test.main();
// board_test.main();
// empty_document_test.main();
// smart_menu_test.main();
@@ -16,7 +16,7 @@ void main() {
await tester.tapGoButton();
// expect to see a readme page
tester.expectToSeePageName(readme);
tester.expectToSeePageName(gettingStarted);
// mock the file picker
final path = await mockSaveFilePath(
@@ -42,12 +42,16 @@ void main() {
final context = await tester.initializeAppFlowy();
await tester.tapGoButton();
// expect to see a readme page
tester.expectToSeePageName(readme);
// expect to see a getting started page
tester.expectToSeePageName(gettingStarted);
// rename the document
await tester.hoverOnPageName(readme);
await tester.renamePage('example');
await tester.hoverOnPageName(
gettingStarted,
onHover: () async {
await tester.renamePage('example');
},
);
final shareButton = find.byType(ShareActionList);
final shareButtonState =
@@ -90,6 +94,13 @@ const expectedMarkdown = r'''
1. Keyboard shortcuts [guide](https://appflowy.gitbook.io/docs/essential-documentation/shortcuts)
1. Markdown [reference](https://appflowy.gitbook.io/docs/essential-documentation/markdown)
1. Type `/code` to insert a code block
```rust
// This is the main function.
fn main() {
// Print text to the console.
println!("Hello World!");
}
```
## Have a question❓
> Click `?` at the bottom right for help and support.
@@ -0,0 +1,48 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/folder/personal_folder.dart';
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import '../util/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('sidebar expand test', () {
bool isExpanded({required FolderCategoryType type}) {
if (type == FolderCategoryType.personal) {
return find
.descendant(
of: find.byType(PersonalFolder),
matching: find.byType(ViewItem),
)
.evaluate()
.isNotEmpty;
}
return false;
}
testWidgets('first time the personal folder is expanded', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
// first time is expanded
expect(isExpanded(type: FolderCategoryType.personal), true);
// collapse the personal folder
await tester.tapButton(
find.byTooltip(LocaleKeys.sideBar_clickToHidePersonal.tr()),
);
expect(isExpanded(type: FolderCategoryType.personal), false);
// expand the personal folder
await tester.tapButton(
find.byTooltip(LocaleKeys.sideBar_clickToHidePersonal.tr()),
);
expect(isExpanded(type: FolderCategoryType.personal), true);
});
});
}
@@ -0,0 +1,222 @@
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/folder/favorite_folder.dart';
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import '../util/base.dart';
import '../util/common_operations.dart';
import '../util/expectation.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('Favorites', () {
testWidgets(
'Toggle favorites for views creates / removes the favorite header along with favorite views',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
// no favorite folder
expect(find.byType(FavoriteFolder), findsNothing);
// create the nested views
final names = [
1,
2,
].map((e) => 'document_$e').toList();
for (var i = 0; i < names.length; i++) {
final parentName = i == 0 ? gettingStarted : names[i - 1];
await tester.createNewPageWithName(
name: names[i],
parentName: parentName,
layout: ViewLayoutPB.Document,
);
tester.expectToSeePageName(
names[i],
parentName: parentName,
layout: ViewLayoutPB.Document,
parentLayout: ViewLayoutPB.Document,
);
}
await tester.favoriteViewByName(gettingStarted);
expect(
tester.findFavoritePageName(gettingStarted),
findsOneWidget,
);
await tester.favoriteViewByName(names[1]);
expect(
tester.findFavoritePageName(names[1]),
findsNWidgets(2),
);
await tester.unfavoriteViewByName(gettingStarted);
expect(
tester.findFavoritePageName(gettingStarted),
findsNothing,
);
expect(
tester.findFavoritePageName(
names[1],
),
findsOneWidget,
);
await tester.unfavoriteViewByName(names[1]);
expect(
tester.findFavoritePageName(
names[1],
),
findsNothing,
);
});
testWidgets(
'renaming a favorite view updates name under favorite header',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
const name = 'test';
await tester.favoriteViewByName(gettingStarted);
await tester.hoverOnPageName(
gettingStarted,
layout: ViewLayoutPB.Document,
onHover: () async {
await tester.renamePage(name);
await tester.pumpAndSettle();
},
);
expect(
tester.findPageName(name),
findsNWidgets(2),
);
expect(
tester.findFavoritePageName(name),
findsNothing,
);
},
);
testWidgets(
'deleting first level favorite view removes its instance from favorite header, deleting root level views leads to removal of all favorites that are its children',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
final names = [1, 2].map((e) => 'document_$e').toList();
for (var i = 0; i < names.length; i++) {
final parentName = i == 0 ? gettingStarted : names[i - 1];
await tester.createNewPageWithName(
name: names[i],
parentName: parentName,
layout: ViewLayoutPB.Document,
);
tester.expectToSeePageName(names[i], parentName: parentName);
}
await tester.favoriteViewByName(gettingStarted);
await tester.favoriteViewByName(names[0]);
await tester.favoriteViewByName(names[1]);
expect(
find.byWidgetPredicate(
(widget) =>
widget is ViewItem &&
widget.view.isFavorite &&
widget.categoryType == FolderCategoryType.favorite,
),
findsNWidgets(6),
);
await tester.hoverOnPageName(
names[1],
layout: ViewLayoutPB.Document,
onHover: () async {
await tester.tapDeletePageButton();
await tester.pumpAndSettle();
},
);
expect(
find.byWidgetPredicate(
(widget) =>
widget is ViewItem &&
widget.view.isFavorite &&
widget.categoryType == FolderCategoryType.favorite,
),
findsNWidgets(3),
);
await tester.hoverOnPageName(
gettingStarted,
layout: ViewLayoutPB.Document,
onHover: () async {
await tester.tapDeletePageButton();
await tester.pumpAndSettle();
},
);
expect(
find.byWidgetPredicate(
(widget) =>
widget is ViewItem &&
widget.view.isFavorite &&
widget.categoryType == FolderCategoryType.favorite,
),
findsNothing,
);
},
);
testWidgets(
'view selection is synced between favorites and personal folder',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.createNewPageWithName();
await tester.favoriteViewByName(gettingStarted);
expect(
find.byWidgetPredicate(
(widget) =>
widget is FlowyHover &&
widget.isSelected != null &&
widget.isSelected!(),
),
findsNWidgets(2),
);
},
);
testWidgets(
'context menu opens up for favorites',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.createNewPageWithName();
await tester.favoriteViewByName(gettingStarted);
await tester.hoverOnPageName(
gettingStarted,
layout: ViewLayoutPB.Document,
useLast: false,
onHover: () async {
await tester.tapPageOptionButton();
await tester.pumpAndSettle();
expect(
find.byType(PopoverContainer),
findsOneWidget,
);
},
);
await tester.pumpAndSettle();
},
);
});
}
@@ -0,0 +1,209 @@
import 'package:appflowy/plugins/database_view/board/presentation/board_page.dart';
import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_page.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart';
import 'package:appflowy/workspace/presentation/home/menu/view/draggable_view_item.dart';
import 'package:appflowy/workspace/presentation/home/menu/view/view_add_button.dart';
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
import 'package:appflowy/workspace/presentation/home/menu/view/view_more_action_button.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import '../util/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('sidebar test', () {
testWidgets('create a new page', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
// create a new page
const name = 'Hello AppFlowy';
await tester.tapNewPageButton();
await tester.enterText(find.byType(TextFormField), name);
await tester.tapOKButton();
// expect to see a new document
tester.expectToSeePageName(
name,
);
// and with one paragraph block
expect(find.byType(TextBlockComponentWidget), findsOneWidget);
});
testWidgets('create a new document, grid, board and calendar',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
for (final layout in ViewLayoutPB.values) {
// create a new page
final name = 'AppFlowy_$layout';
await tester.createNewPageWithName(
name: name,
layout: layout,
);
// expect to see a new page
tester.expectToSeePageName(
name,
layout: layout,
);
switch (layout) {
case ViewLayoutPB.Document:
// and with one paragraph block
expect(find.byType(TextBlockComponentWidget), findsOneWidget);
break;
case ViewLayoutPB.Grid:
expect(find.byType(GridPage), findsOneWidget);
break;
case ViewLayoutPB.Board:
expect(find.byType(BoardPage), findsOneWidget);
break;
case ViewLayoutPB.Calendar:
expect(find.byType(CalendarPage), findsOneWidget);
break;
}
await tester.openPage(gettingStarted);
}
});
testWidgets('create some nested pages, and move them', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
final names = [1, 2, 3, 4].map((e) => 'document_$e').toList();
for (var i = 0; i < names.length; i++) {
final parentName = i == 0 ? gettingStarted : names[i - 1];
await tester.createNewPageWithName(
name: names[i],
parentName: parentName,
layout: ViewLayoutPB.Document,
);
tester.expectToSeePageName(names[i], parentName: parentName);
}
// move the document_3 to the getting started page
await tester.movePageToOtherPage(
name: names[3],
parentName: gettingStarted,
layout: ViewLayoutPB.Document,
parentLayout: ViewLayoutPB.Document,
);
final fromId = tester
.widget<SingleInnerViewItem>(tester.findPageName(names[3]))
.view
.parentViewId;
final toId = tester
.widget<SingleInnerViewItem>(tester.findPageName(gettingStarted))
.view
.id;
expect(fromId, toId);
// move the document_2 before document_1
await tester.movePageToOtherPage(
name: names[2],
parentName: gettingStarted,
layout: ViewLayoutPB.Document,
parentLayout: ViewLayoutPB.Document,
position: DraggableHoverPosition.bottom,
);
final childViews = tester
.widget<SingleInnerViewItem>(tester.findPageName(gettingStarted))
.view
.childViews;
expect(
childViews[0].id,
tester
.widget<SingleInnerViewItem>(tester.findPageName(names[2]))
.view
.id,
);
expect(
childViews[1].id,
tester
.widget<SingleInnerViewItem>(tester.findPageName(names[0]))
.view
.id,
);
expect(
childViews[2].id,
tester
.widget<SingleInnerViewItem>(tester.findPageName(names[3]))
.view
.id,
);
});
testWidgets('unable to move a document into a database', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
const document = 'document';
await tester.createNewPageWithName(
name: document,
openAfterCreated: false,
);
tester.expectToSeePageName(document, layout: ViewLayoutPB.Document);
const grid = 'grid';
await tester.createNewPageWithName(
name: grid,
layout: ViewLayoutPB.Grid,
openAfterCreated: false,
);
tester.expectToSeePageName(grid, layout: ViewLayoutPB.Grid);
// move the document to the grid page
await tester.movePageToOtherPage(
name: document,
parentName: grid,
layout: ViewLayoutPB.Document,
parentLayout: ViewLayoutPB.Grid,
);
// it should not be moved
final childViews = tester
.widget<SingleInnerViewItem>(tester.findPageName(gettingStarted))
.view
.childViews;
expect(
childViews[0].name,
document,
);
expect(
childViews[1].name,
grid,
);
});
testWidgets('unable to create a new database inside the existing one',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
const grid = 'grid';
await tester.createNewPageWithName(
name: grid,
layout: ViewLayoutPB.Grid,
openAfterCreated: true,
);
tester.expectToSeePageName(grid, layout: ViewLayoutPB.Grid);
await tester.hoverOnPageName(
grid,
layout: ViewLayoutPB.Grid,
onHover: () async {
expect(find.byType(ViewAddButton), findsNothing);
expect(find.byType(ViewMoreActionButton), findsOneWidget);
},
);
});
});
}
@@ -0,0 +1,14 @@
import 'package:integration_test/integration_test.dart';
import 'sidebar_test.dart' as sidebar_test;
import 'sidebar_expand_test.dart' as sidebar_expanded_test;
import 'sidebar_favorites_test.dart' as sidebar_favorite_test;
void startTesting() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
// Sidebar integration tests
sidebar_test.main();
sidebar_expanded_test.main();
sidebar_favorite_test.main();
}
@@ -1,23 +1,26 @@
import 'dart:io';
import 'package:appflowy/workspace/presentation/home/tabs/flowy_tab.dart';
import 'package:appflowy/workspace/presentation/home/tabs/tabs_manager.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'util/base.dart';
import 'util/common_operations.dart';
import 'util/expectation.dart';
import 'util/keyboard.dart';
const _readmeName = 'Read me';
const _documentName = 'Document';
const _calendarName = 'Calendar';
const _documentName = 'First Doc';
const _documentTwoName = 'Second Doc';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('Tabs', () {
testWidgets('Open AppFlowy and open/navigate multiple tabs',
(tester) async {
testWidgets('Open AppFlowy and open/navigate/close tabs', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
@@ -29,25 +32,21 @@ void main() {
findsNothing,
);
await tester.createNewPageWithName(ViewLayoutPB.Calendar, _calendarName);
await tester.createNewPageWithName(ViewLayoutPB.Document, _documentName);
await tester.createNewPageWithName(
name: _documentName,
layout: ViewLayoutPB.Document,
);
// Navigate current view to "Read me" document again
await tester.tapButtonWithName(_readmeName);
await tester.createNewPageWithName(
name: _documentTwoName,
layout: ViewLayoutPB.Document,
);
/// Open second menu item in a new tab
await tester.openAppInNewTab(_calendarName);
await tester.openAppInNewTab(gettingStarted, ViewLayoutPB.Document);
/// Open third menu item in a new tab
await tester.openAppInNewTab(_documentName);
expect(
find.descendant(
of: find.byType(TabsManager),
matching: find.byType(TabBar),
),
findsOneWidget,
);
await tester.openAppInNewTab(_documentName, ViewLayoutPB.Document);
expect(
find.descendant(
@@ -57,13 +56,32 @@ void main() {
findsNWidgets(3),
);
/// Navigate to the first tab
/// Navigate to the second tab
await tester.tap(
find.descendant(
of: find.byType(FlowyTab),
matching: find.text(_readmeName),
matching: find.text(gettingStarted),
),
);
/// Close tab by shortcut
await FlowyTestKeyboard.simulateKeyDownEvent(
[
Platform.isMacOS
? LogicalKeyboardKey.meta
: LogicalKeyboardKey.control,
LogicalKeyboardKey.keyW,
],
tester: tester,
);
expect(
find.descendant(
of: find.byType(TabBar),
matching: find.byType(FlowyTab),
),
findsNWidgets(2),
);
});
});
}
@@ -91,7 +91,6 @@ extension AppFlowyTestBase on WidgetTester {
warnIfMissed: warnIfMissed,
);
await pumpAndSettle(Duration(milliseconds: milliseconds));
return;
}
Future<void> tapButtonWithName(
@@ -1,15 +1,18 @@
import 'dart:ui';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_new_page_button.dart';
import 'package:appflowy/workspace/presentation/home/menu/view/draggable_view_item.dart';
import 'package:appflowy/workspace/presentation/home/menu/view/view_action_type.dart';
import 'package:appflowy/workspace/presentation/home/menu/view/view_add_button.dart';
import 'package:appflowy/workspace/presentation/home/menu/view/view_more_action_button.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/share/share_button.dart';
import 'package:appflowy/user/presentation/skip_log_in_screen.dart';
import 'package:appflowy/workspace/presentation/home/menu/app/header/add_button.dart';
import 'package:appflowy/workspace/presentation/home/menu/app/section/item.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_language_view.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/widget/buttons/primary_button.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -24,35 +27,48 @@ extension CommonOperations on WidgetTester {
}
/// Tap the + button on the home page.
Future<void> tapAddButton() async {
final addButton = find.byType(AddButton);
await tapButton(addButton);
Future<void> tapAddViewButton({
String name = gettingStarted,
}) async {
await hoverOnPageName(
name,
onHover: () async {
final addButton = find.byType(ViewAddButton);
await tapButton(addButton);
},
);
}
/// Tap the 'New Page' Button on the sidebar.
Future<void> tapNewPageButton() async {
final newPageButton = find.byType(SidebarNewPageButton);
await tapButton(newPageButton);
}
/// Tap the create document button.
///
/// Must call [tapAddButton] first.
/// Must call [tapAddViewButton] first.
Future<void> tapCreateDocumentButton() async {
await tapButtonWithName(LocaleKeys.document_menuName.tr());
}
/// Tap the create grid button.
///
/// Must call [tapAddButton] first.
/// Must call [tapAddViewButton] first.
Future<void> tapCreateGridButton() async {
await tapButtonWithName(LocaleKeys.grid_menuName.tr());
}
/// Tap the create grid button.
///
/// Must call [tapAddButton] first.
/// Must call [tapAddViewButton] first.
Future<void> tapCreateCalendarButton() async {
await tapButtonWithName(LocaleKeys.calendar_menuName.tr());
}
/// Tap the import button.
///
/// Must call [tapAddButton] first.
/// Must call [tapAddViewButton] first.
Future<void> tapImportButton() async {
await tapButtonWithName(LocaleKeys.moreAction_import.tr());
}
@@ -116,6 +132,7 @@ extension CommonOperations on WidgetTester {
Finder finder, {
Offset? offset,
Future<void> Function()? onHover,
bool removePointer = true,
}) async {
try {
final gesture = await createGesture(kind: PointerDeviceKind.mouse);
@@ -133,19 +150,30 @@ extension CommonOperations on WidgetTester {
/// Hover on the page name.
Future<void> hoverOnPageName(
String name, {
ViewLayoutPB layout = ViewLayoutPB.Document,
Future<void> Function()? onHover,
bool useLast = true,
}) async {
final pageNames = findPageName(name, layout: layout);
if (useLast) {
await hoverOnWidget(findPageName(name).last, onHover: onHover);
await hoverOnWidget(
pageNames.last,
onHover: onHover,
);
} else {
await hoverOnWidget(findPageName(name).first, onHover: onHover);
await hoverOnWidget(
pageNames.first,
onHover: onHover,
);
}
}
/// open the page with given name.
Future<void> openPage(String name) async {
final finder = findPageName(name);
Future<void> openPage(
String name, {
ViewLayoutPB layout = ViewLayoutPB.Document,
}) async {
final finder = findPageName(name, layout: layout);
expect(finder, findsOneWidget);
await tapButton(finder);
}
@@ -154,20 +182,38 @@ extension CommonOperations on WidgetTester {
///
/// Must call [hoverOnPageName] first.
Future<void> tapPageOptionButton() async {
final optionButton = find.byType(ViewDisclosureButton);
final optionButton = find.byType(ViewMoreActionButton);
await tapButton(optionButton);
}
/// Tap the delete page button.
Future<void> tapDeletePageButton() async {
await tapPageOptionButton();
await tapButtonWithName(ViewDisclosureAction.delete.name);
await tapButtonWithName(ViewMoreActionType.delete.name);
}
/// Tap the rename page button.
Future<void> tapRenamePageButton() async {
await tapPageOptionButton();
await tapButtonWithName(ViewDisclosureAction.rename.name);
await tapButtonWithName(ViewMoreActionType.rename.name);
}
/// Tap the favorite page button
Future<void> tapFavoritePageButton() async {
await tapPageOptionButton();
await tapButtonWithName(ViewMoreActionType.favorite.name);
}
/// Tap the unfavorite page button
Future<void> tapUnfavoritePageButton() async {
await tapPageOptionButton();
await tapButtonWithName(ViewMoreActionType.unFavorite.name);
}
/// Tap the Open in a new tab button
Future<void> tapOpenInTabButton() async {
await tapPageOptionButton();
await tapButtonWithName(ViewMoreActionType.openInNewTab.name);
}
/// Rename the page.
@@ -224,12 +270,14 @@ extension CommonOperations on WidgetTester {
await tapButton(markdownButton);
}
Future<void> createNewPageWithName(
ViewLayoutPB layout, [
Future<void> createNewPageWithName({
String? name,
]) async {
ViewLayoutPB layout = ViewLayoutPB.Document,
String? parentName,
bool openAfterCreated = true,
}) async {
// create a new page
await tapAddButton();
await tapAddViewButton(name: parentName ?? gettingStarted);
await tapButtonWithName(layout.menuName);
await pumpAndSettle();
@@ -237,6 +285,7 @@ extension CommonOperations on WidgetTester {
if (name != null) {
await hoverOnPageName(
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
layout: layout,
onHover: () async {
await renamePage(name);
await pumpAndSettle();
@@ -244,6 +293,16 @@ extension CommonOperations on WidgetTester {
);
await pumpAndSettle();
}
// open the page after created
if (openAfterCreated) {
await openPage(
// if the name is null, use the default name
name ?? LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
layout: layout,
);
await pumpAndSettle();
}
}
Future<void> simulateKeyEvent(
@@ -282,11 +341,72 @@ extension CommonOperations on WidgetTester {
await pumpAndSettle();
}
Future<void> openAppInNewTab(String name) async {
await hoverOnPageName(name);
await tap(find.byType(ViewDisclosureButton));
Future<void> openAppInNewTab(String name, ViewLayoutPB layout) async {
await hoverOnPageName(
name,
onHover: () async {
await tapOpenInTabButton();
await pumpAndSettle();
},
);
await pumpAndSettle();
await tap(find.text(LocaleKeys.disclosureAction_openNewTab.tr()));
}
Future<void> favoriteViewByName(
String name, {
ViewLayoutPB layout = ViewLayoutPB.Document,
}) async {
await hoverOnPageName(
name,
layout: layout,
useLast: true,
onHover: () async {
await tapFavoritePageButton();
await pumpAndSettle();
},
);
}
Future<void> unfavoriteViewByName(
String name, {
ViewLayoutPB layout = ViewLayoutPB.Document,
}) async {
await hoverOnPageName(
name,
layout: layout,
useLast: true,
onHover: () async {
await tapUnfavoritePageButton();
await pumpAndSettle();
},
);
}
Future<void> movePageToOtherPage({
required String name,
required String parentName,
required ViewLayoutPB layout,
required ViewLayoutPB parentLayout,
DraggableHoverPosition position = DraggableHoverPosition.center,
}) async {
final from = findPageName(name, layout: layout);
final to = findPageName(parentName, layout: parentLayout);
final gesture = await startGesture(getCenter(from));
Offset offset = Offset.zero;
switch (position) {
case DraggableHoverPosition.center:
offset = getCenter(to);
break;
case DraggableHoverPosition.top:
offset = getTopLeft(to);
break;
case DraggableHoverPosition.bottom:
offset = getBottomLeft(to);
break;
default:
}
await gesture.moveTo(offset, timeStamp: const Duration(milliseconds: 400));
await gesture.up();
await pumpAndSettle();
}
}
@@ -6,6 +6,7 @@ import 'package:appflowy/plugins/database_view/calendar/application/calendar_blo
import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_day.dart';
import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_page.dart';
import 'package:appflowy/plugins/database_view/calendar/presentation/toolbar/calendar_layout_setting.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/checkbox.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/checklist/checklist.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/select_option/option_list.dart';
@@ -14,31 +15,42 @@ import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/create_filter_list.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/disclosure_button.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/filter_menu_item.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/footer/grid_footer.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_cell.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_cell_action_sheet.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_editor.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_extension.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_list.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_option_editor.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/type_option/date.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/row/row.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/create_sort_list.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/order_panel.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/sort_editor.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/sort_menu.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/type_option/date.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/filter_button.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/grid_layout.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/sort_button.dart';
import 'package:appflowy/plugins/database_view/tar_bar/tab_bar_view.dart';
import 'package:appflowy/plugins/database_view/tar_bar/tab_bar_header.dart';
import 'package:appflowy/plugins/database_view/tar_bar/tar_bar_add_button.dart';
import 'package:appflowy/plugins/database_view/widgets/database_layout_ext.dart';
import 'package:appflowy/plugins/database_view/widgets/row/accessory/cell_accessory.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/cells.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/checklist_cell/checklist_progress_bar.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/date_cell/date_editor.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/select_option_editor.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/text_field.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/checklist_cell/checklist_progress_bar.dart';
import 'package:appflowy/plugins/database_view/widgets/row/row_action.dart';
import 'package:appflowy/plugins/database_view/widgets/row/row_banner.dart';
import 'package:appflowy/plugins/database_view/widgets/row/row_detail.dart';
import 'package:appflowy/plugins/database_view/widgets/row/row_document.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/date_cell/date_editor.dart';
import 'package:appflowy/plugins/database_view/widgets/setting/database_setting.dart';
import 'package:appflowy/plugins/database_view/widgets/setting/setting_button.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/emoji_picker/emoji_menu_item.dart';
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:calendar_view/calendar_view.dart';
@@ -50,25 +62,12 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/footer/grid_footer.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_cell.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_editor.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/row/row.dart';
import 'package:appflowy/plugins/database_view/widgets/row/accessory/cell_accessory.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/cells.dart';
import 'package:appflowy/plugins/database_view/widgets/row/row_action.dart';
import 'package:appflowy/plugins/database_view/widgets/row/row_banner.dart';
import 'package:appflowy/plugins/database_view/widgets/row/row_detail.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/emoji_picker/emoji_menu_item.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
import 'package:path/path.dart' as p;
import 'package:table_calendar/table_calendar.dart';
import 'base.dart';
import 'common_operations.dart';
import 'expectation.dart';
import 'package:path/path.dart' as p;
import 'mock/mock_file_picker.dart';
extension AppFlowyDatabaseTest on WidgetTester {
@@ -77,9 +76,9 @@ extension AppFlowyDatabaseTest on WidgetTester {
await tapGoButton();
// expect to see a readme page
expectToSeePageName(readme);
expectToSeePageName(gettingStarted);
await tapAddButton();
await tapAddViewButton();
await tapImportButton();
final testFileNames = ['v020.afdb'];
@@ -103,7 +102,8 @@ extension AppFlowyDatabaseTest on WidgetTester {
paths: paths,
);
await tapDatabaseRawDataButton();
await openPage('v020');
await pumpAndSettle();
await openPage('v020', layout: ViewLayoutPB.Grid);
}
Future<void> hoverOnFirstRowOfGrid() async {
@@ -358,6 +358,16 @@ extension AppFlowyDatabaseTest on WidgetTester {
await tapButton(findNewDateFormat);
}
Future<void> clearDate() async {
final findDateEditor = find.byType(DateCellEditor);
final findClearButton = find.byType(ClearDateButton);
final finder = find.descendant(
of: findDateEditor,
matching: findClearButton,
);
await tapButton(finder);
}
Future<void> tapSelectOptionCellInGrid({
required int rowIndex,
required FieldType fieldType,
@@ -87,7 +87,7 @@ class EditorOperations {
Future<void> switchSolidColorBackground() async {
final findPurpleButton = find.byWidgetPredicate(
(widget) => widget is ColorItem && widget.option.colorHex == "ffe8e0ff",
(widget) => widget is ColorItem && widget.option.name == 'Purple',
);
await tester.tapButton(findPurpleButton);
}
@@ -2,30 +2,53 @@ import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/banner.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/document_header_node_widget.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart';
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
import 'package:appflowy/workspace/presentation/home/home_stack.dart';
import 'package:appflowy/workspace/presentation/home/menu/app/section/item.dart';
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter_test/flutter_test.dart';
const String readme = 'Read me';
// const String readme = 'Read me';
const String gettingStarted = '⭐️ Getting started';
extension Expectation on WidgetTester {
/// Expect to see the home page and with a default read me page.
void expectToSeeHomePage() {
expect(find.byType(HomeStack), findsOneWidget);
expect(find.textContaining(readme), findsWidgets);
expect(find.textContaining(gettingStarted), findsWidgets);
}
/// Expect to see the page name on the home page.
void expectToSeePageName(String name) {
final pageName = findPageName(name);
void expectToSeePageName(
String name, {
String? parentName,
ViewLayoutPB layout = ViewLayoutPB.Document,
ViewLayoutPB parentLayout = ViewLayoutPB.Document,
}) {
final pageName = findPageName(
name,
layout: layout,
parentName: parentName,
parentLayout: parentLayout,
);
expect(pageName, findsOneWidget);
}
/// Expect not to see the page name on the home page.
void expectNotToSeePageName(String name) {
final pageName = findPageName(name);
void expectNotToSeePageName(
String name, {
String? parentName,
ViewLayoutPB layout = ViewLayoutPB.Document,
ViewLayoutPB parentLayout = ViewLayoutPB.Document,
}) {
final pageName = findPageName(
name,
layout: layout,
parentName: parentName,
parentLayout: parentLayout,
);
expect(pageName, findsNothing);
}
@@ -125,11 +148,49 @@ extension Expectation on WidgetTester {
expect(textWidget, findsOneWidget);
}
/// Find the page name on the home page.
Finder findPageName(String name) {
/// Find if the page is favorite
Finder findFavoritePageName(
String name, {
ViewLayoutPB layout = ViewLayoutPB.Document,
String? parentName,
ViewLayoutPB parentLayout = ViewLayoutPB.Document,
}) {
return find.byWidgetPredicate(
(widget) => widget is ViewSectionItem && widget.view.name == name,
(widget) =>
widget is ViewItem &&
widget.view.isFavorite &&
widget.categoryType == FolderCategoryType.favorite &&
widget.view.name == name &&
widget.view.layout == layout,
skipOffstage: false,
);
}
Finder findPageName(
String name, {
ViewLayoutPB layout = ViewLayoutPB.Document,
String? parentName,
ViewLayoutPB parentLayout = ViewLayoutPB.Document,
}) {
if (parentName == null) {
return find.byWidgetPredicate(
(widget) =>
widget is SingleInnerViewItem &&
widget.view.name == name &&
widget.view.layout == layout,
skipOffstage: false,
);
}
return find.descendant(
of: find.byWidgetPredicate(
(widget) =>
widget is ViewItem &&
widget.view.name == parentName &&
widget.view.layout == parentLayout,
skipOffstage: false,
),
matching: findPageName(name, layout: layout),
);
}
}
@@ -77,13 +77,6 @@ Future<String> mockSaveFilePath(
Future<List<String>> mockPickFilePaths({
required List<String> paths,
}) async {
// late final Directory dir;
// if (customPath != null) {
// dir = Directory(customPath);
// } else {
// dir = await TestFolder.testLocation(applicationDataPath, name);
// }
// final paths = fileNames.map((e) => p.join(dir.path, e)).toList();
getIt.unregister<FilePickerService>();
getIt.registerFactory<FilePickerService>(
() => MockFilePicker(
@@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>9.0</string>
<string>11.0</string>
</dict>
</plist>
+1 -1
View File
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
# platform :ios, '11.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+134 -52
View File
@@ -1,81 +1,163 @@
PODS:
- flowy_editor (0.0.1):
- Flutter
- flowy_infra_ui (0.0.1):
- app_links (0.0.1):
- Flutter
- appflowy_backend (0.0.1):
- Flutter
- connectivity_plus (0.0.1):
- Flutter
- ReachabilitySwift
- device_info_plus (0.0.1):
- Flutter
- DKImagePickerController/Core (4.3.4):
- DKImagePickerController/ImageDataManager
- DKImagePickerController/Resource
- DKImagePickerController/ImageDataManager (4.3.4)
- DKImagePickerController/PhotoGallery (4.3.4):
- DKImagePickerController/Core
- DKPhotoGallery
- DKImagePickerController/Resource (4.3.4)
- DKPhotoGallery (0.0.17):
- DKPhotoGallery/Core (= 0.0.17)
- DKPhotoGallery/Model (= 0.0.17)
- DKPhotoGallery/Preview (= 0.0.17)
- DKPhotoGallery/Resource (= 0.0.17)
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Core (0.0.17):
- DKPhotoGallery/Model
- DKPhotoGallery/Preview
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Model (0.0.17):
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Preview (0.0.17):
- DKPhotoGallery/Model
- DKPhotoGallery/Resource
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Resource (0.0.17):
- SDWebImage
- SwiftyGif
- file_picker (0.0.1):
- DKImagePickerController/PhotoGallery
- Flutter
- flowy_infra_ui (0.0.1):
- Flutter
- Flutter (1.0.0)
- flutter_inappwebview (0.0.1):
- fluttertoast (0.0.2):
- Flutter
- flutter_inappwebview/Core (= 0.0.1)
- OrderedSet (~> 5.0)
- flutter_inappwebview/Core (0.0.1):
- Toast
- integration_test (0.0.1):
- Flutter
- OrderedSet (~> 5.0)
- flutter_keyboard_visibility (0.0.1):
- package_info_plus (0.4.5):
- Flutter
- image_picker (0.0.1):
- path_provider_foundation (0.0.1):
- Flutter
- OrderedSet (5.0.0)
- path_provider (0.0.1):
- FlutterMacOS
- ReachabilitySwift (5.0.0)
- rich_clipboard_ios (0.0.1):
- Flutter
- url_launcher (0.0.1):
- SDWebImage (5.14.2):
- SDWebImage/Core (= 5.14.2)
- SDWebImage/Core (5.14.2)
- shared_preferences_foundation (0.0.1):
- Flutter
- video_player (0.0.1):
- FlutterMacOS
- sign_in_with_apple (0.0.1):
- Flutter
- SwiftyGif (5.4.3)
- Toast (4.0.0)
- url_launcher_ios (0.0.1):
- Flutter
- webview_flutter_wkwebview (0.0.1):
- Flutter
DEPENDENCIES:
- flowy_editor (from `.symlinks/plugins/flowy_editor/ios`)
- flowy_infra_ui (from `.symlinks/plugins/flowy_infra_ui/ios`)
- app_links (from `.symlinks/plugins/app_links/ios`)
- appflowy_backend (from `.symlinks/plugins/appflowy_backend/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- flowy_infra_ui (from `.symlinks/plugins/flowy_infra_ui/ios`)
- Flutter (from `Flutter`)
- flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`)
- flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
- image_picker (from `.symlinks/plugins/image_picker/ios`)
- path_provider (from `.symlinks/plugins/path_provider/ios`)
- url_launcher (from `.symlinks/plugins/url_launcher/ios`)
- video_player (from `.symlinks/plugins/video_player/ios`)
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- integration_test (from `.symlinks/plugins/integration_test/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- rich_clipboard_ios (from `.symlinks/plugins/rich_clipboard_ios/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sign_in_with_apple (from `.symlinks/plugins/sign_in_with_apple/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`)
SPEC REPOS:
trunk:
- OrderedSet
- DKImagePickerController
- DKPhotoGallery
- ReachabilitySwift
- SDWebImage
- SwiftyGif
- Toast
EXTERNAL SOURCES:
flowy_editor:
:path: ".symlinks/plugins/flowy_editor/ios"
flowy_infra_ui:
:path: ".symlinks/plugins/flowy_infra_ui/ios"
app_links:
:path: ".symlinks/plugins/app_links/ios"
appflowy_backend:
:path: ".symlinks/plugins/appflowy_backend/ios"
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios"
file_picker:
:path: ".symlinks/plugins/file_picker/ios"
flowy_infra_ui:
:path: ".symlinks/plugins/flowy_infra_ui/ios"
Flutter:
:path: Flutter
flutter_inappwebview:
:path: ".symlinks/plugins/flutter_inappwebview/ios"
flutter_keyboard_visibility:
:path: ".symlinks/plugins/flutter_keyboard_visibility/ios"
image_picker:
:path: ".symlinks/plugins/image_picker/ios"
path_provider:
:path: ".symlinks/plugins/path_provider/ios"
url_launcher:
:path: ".symlinks/plugins/url_launcher/ios"
video_player:
:path: ".symlinks/plugins/video_player/ios"
fluttertoast:
:path: ".symlinks/plugins/fluttertoast/ios"
integration_test:
:path: ".symlinks/plugins/integration_test/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
rich_clipboard_ios:
:path: ".symlinks/plugins/rich_clipboard_ios/ios"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
sign_in_with_apple:
:path: ".symlinks/plugins/sign_in_with_apple/ios"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
webview_flutter_wkwebview:
:path: ".symlinks/plugins/webview_flutter_wkwebview/ios"
SPEC CHECKSUMS:
flowy_editor: bf8d58894ddb03453bd4d8521c57267ad638b837
flowy_infra_ui: 146c88346fd55d2ee6a41ae35059a5bf095cfbb3
appflowy_backend: c416222c639e678828776789bf0c1a1d0d59df3c
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
image_picker: 9aa50e1d8cdacdbed739e925b7eea16d014367e6
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
video_player: ecd305f42e9044793efd34846e1ce64c31ea6fcb
app_links: 5ef33d0d295a89d9d16bb81b0e3b0d5f70d6c875
appflowy_backend: 144c20d8bfb298c4e10fa3fa6701a9f41bf98b88
connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a
device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
file_picker: ce3938a0df3cc1ef404671531facef740d03f920
flowy_infra_ui: 0455e1fa8c51885aa1437848e361e99419f34ebc
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
fluttertoast: fafc4fa4d01a6a9e4f772ecd190ffa525e9e2d9c
integration_test: 13825b8a9334a850581300559b8839134b124670
package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7
path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
rich_clipboard_ios: 7588abe18f881a6d0e9ec0b12e51cae2761e8942
SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84
shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c
sign_in_with_apple: f3bf75217ea4c2c8b91823f225d70230119b8440
SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a
PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
COCOAPODS: 1.10.1
COCOAPODS: 1.11.3
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
@@ -217,10 +217,12 @@
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
@@ -231,6 +233,7 @@
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
@@ -340,7 +343,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -414,7 +417,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -463,7 +466,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -47,5 +47,7 @@
</array>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>
@@ -29,4 +29,16 @@ class KVKeys {
'kDocumentAppearanceFontSize';
static const String kDocumentAppearanceFontFamily =
'kDocumentAppearanceFontFamily';
/// The key for saving the expanded views
///
/// The value is a json string with the following format:
/// {'viewId': true, 'viewId2': false}
static const String expandedViews = 'expandedViews';
/// The key for saving the expanded folder
///
/// The value is a json string with the following format:
/// {'SidebarFolderCategoryType.value': true}
static const String expandedFolders = 'expandedFolders';
}
@@ -1,9 +1,9 @@
import 'dart:async';
import 'package:appflowy_backend/protobuf/flowy-user/user_setting.pb.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/protobuf/flowy-net/network_state.pb.dart';
import 'package:flutter/services.dart';
class NetworkListener {
@@ -46,12 +46,12 @@ class NetworkListener {
return NetworkTypePB.VPN;
case ConnectivityResult.none:
case ConnectivityResult.other:
return NetworkTypePB.Unknown;
return NetworkTypePB.NetworkUnknown;
}
}();
Log.info("Network type: $networkType");
final state = NetworkStatePB.create()..ty = networkType;
NetworkEventUpdateNetworkType(state).send().then((result) {
UserEventUpdateNetworkState(state).send().then((result) {
result.fold(
(l) {},
(e) => Log.error(e),
@@ -8,4 +8,12 @@ extension RawKeyboardExtension on RawKeyboard {
LogicalKeyboardKey.altRight,
].contains(key),
);
bool get isControlPressed => keysPressed.any(
(key) => [
LogicalKeyboardKey.control,
LogicalKeyboardKey.controlLeft,
LogicalKeyboardKey.controlRight,
].contains(key),
);
}
+12 -50
View File
@@ -1,4 +1,5 @@
// lib/env/env.dart
import 'package:appflowy/startup/startup.dart';
import 'package:envied/envied.dart';
part 'env.g.dart';
@@ -28,61 +29,22 @@ abstract class Env {
defaultValue: '',
)
static final String supabaseAnonKey = _Env.supabaseAnonKey;
@EnviedField(
obfuscate: true,
varName: 'SUPABASE_KEY',
defaultValue: '',
)
static final String supabaseKey = _Env.supabaseKey;
@EnviedField(
obfuscate: true,
varName: 'SUPABASE_JWT_SECRET',
defaultValue: '',
)
static final String supabaseJwtSecret = _Env.supabaseJwtSecret;
@EnviedField(
obfuscate: true,
varName: 'SUPABASE_DB',
defaultValue: '',
)
static final String supabaseDb = _Env.supabaseDb;
@EnviedField(
obfuscate: true,
varName: 'SUPABASE_DB_USER',
defaultValue: '',
)
static final String supabaseDbUser = _Env.supabaseDbUser;
@EnviedField(
obfuscate: true,
varName: 'SUPABASE_DB_PASSWORD',
defaultValue: '',
)
static final String supabaseDbPassword = _Env.supabaseDbPassword;
@EnviedField(
obfuscate: true,
varName: 'SUPABASE_DB_PORT',
defaultValue: '5432',
)
static final String supabaseDbPort = _Env.supabaseDbPort;
@EnviedField(
obfuscate: true,
varName: 'ENABLE_SUPABASE_SYNC',
defaultValue: true,
)
static final bool enableSupabaseSync = _Env.enableSupabaseSync;
}
bool get isSupabaseEnable => false;
// Env.supabaseUrl.isNotEmpty &&
// Env.supabaseAnonKey.isNotEmpty &&
// Env.supabaseKey.isNotEmpty &&
// Env.supabaseJwtSecret.isNotEmpty &&
// Env.supabaseDb.isNotEmpty &&
// Env.supabaseDbUser.isNotEmpty &&
// Env.supabaseDbPassword.isNotEmpty &&
// Env.supabaseDbPort.isNotEmpty;
bool get isSupabaseEnabled {
// Only enable supabase in release and develop mode.
if (integrationEnv().isRelease || integrationEnv().isDevelop) {
return Env.supabaseUrl.isNotEmpty &&
Env.supabaseAnonKey.isNotEmpty &&
Env.supabaseJwtSecret.isNotEmpty;
} else {
return false;
}
}
@@ -46,7 +46,8 @@ class BlankPagePluginWidgetBuilder extends PluginWidgetBuilder
Widget tabBarItem(String pluginId) => leftBarItem;
@override
Widget buildWidget({PluginContext? context}) => const BlankPage();
Widget buildWidget({PluginContext? context, required bool shrinkWrap}) =>
const BlankPage();
@override
List<NavigationItem> get navigationItems => [this];
@@ -1,4 +1,5 @@
import 'dart:async';
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'package:appflowy/plugins/database_view/application/field/field_listener.dart';
import 'package:appflowy/plugins/database_view/application/row/row_meta_listener.dart';
import 'package:appflowy/plugins/database_view/application/row/row_service.dart';
@@ -8,7 +9,6 @@ import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import '../field/field_controller.dart';
import '../field/field_service.dart';
import '../field/type_option/type_option_context.dart';
import 'cell_listener.dart';
@@ -104,7 +104,7 @@ class CellController<T, D> extends Equatable {
);
// Only the primary can listen on the row meta changes.
if (_cellContext.fieldInfo.isPrimary) {
if (_cellContext.fieldInfo.field.isPrimary) {
_rowMetaListener?.start(
callback: (newRowMeta) {
_cellContext = _cellContext.copyWith(rowMeta: newRowMeta);
@@ -35,6 +35,7 @@ class DateCellData with _$DateCellData {
DateTime? dateTime,
String? time,
required bool includeTime,
bool? clearFlag,
}) = _DateCellData;
}
@@ -55,6 +56,9 @@ class DateCellDataPersistence implements CellDataPersistence<DateCellData> {
if (data.time != null) {
payload.time = data.time!;
}
if (data.clearFlag != null) {
payload.clearFlag = data.clearFlag!;
}
payload.includeTime = data.includeTime;
return DatabaseEventUpdateDateCell(payload).send().then((result) {
@@ -15,7 +15,7 @@ import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:convert' show utf8;
import '../field/field_controller.dart';
import '../field/field_info.dart';
import '../row/row_service.dart';
part 'cell_service.freezed.dart';
part 'cell_data_loader.dart';
@@ -62,14 +62,14 @@ class DatabaseCellContext with _$DatabaseCellContext {
String get rowId => rowMeta.id;
String get fieldId => fieldInfo.id;
String get fieldId => fieldInfo.field.id;
FieldType get fieldType => fieldInfo.fieldType;
FieldType get fieldType => fieldInfo.field.fieldType;
ValueKey key() {
return ValueKey("${rowMeta.id}$fieldId${fieldInfo.fieldType}");
return ValueKey("${rowMeta.id}$fieldId${fieldInfo.field.fieldType}");
}
/// Only the primary field can have an emoji.
String? get emoji => fieldInfo.isPrimary ? rowMeta.icon : null;
String? get emoji => fieldInfo.field.isPrimary ? rowMeta.icon : null;
}
@@ -13,8 +13,10 @@ import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:collection/collection.dart';
import 'dart:async';
import 'package:dartz/dartz.dart';
import 'package:flutter/material.dart';
import 'database_view_service.dart';
import 'defines.dart';
import 'field/field_info.dart';
import 'layout/layout_service.dart';
import 'layout/layout_setting_listener.dart';
import 'row/row_cache.dart';
@@ -92,6 +94,8 @@ class DatabaseController {
final DatabaseGroupListener _groupListener;
final DatabaseLayoutSettingListener _layoutListener;
final ValueNotifier<bool> _isLoading = ValueNotifier(true);
DatabaseController({required ViewPB view})
: viewId = view.id,
_databaseViewBackendSvc = DatabaseViewBackendService(viewId: view.id),
@@ -109,6 +113,12 @@ class DatabaseController {
_listenOnLayoutChanged();
}
void setIsLoading(bool isLoading) {
_isLoading.value = isLoading;
}
ValueNotifier<bool> get isLoading => _isLoading;
void addListener({
DatabaseCallbacks? onDatabaseChanged,
DatabaseLayoutSettingCallbacks? onLayoutChanged,
@@ -3,12 +3,16 @@ import 'dart:collection';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/sort_info.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import '../grid/presentation/widgets/filter/filter_info.dart';
import 'field/field_controller.dart';
import 'field/field_info.dart';
import 'row/row_cache.dart';
import 'row/row_service.dart';
part 'defines.freezed.dart';
typedef OnFieldsChanged = void Function(UnmodifiableListView<FieldInfo>);
typedef OnFiltersChanged = void Function(List<FilterInfo>);
typedef OnSortsChanged = void Function(List<SortInfo>);
@@ -27,3 +31,11 @@ typedef OnNumOfRowsChanged = void Function(
);
typedef OnError = void Function(FlowyError);
@freezed
class LoadingState with _$LoadingState {
const factory LoadingState.loading() = _Loading;
const factory LoadingState.finish(
Either<Unit, FlowyError> successOrFail,
) = _Finish;
}
@@ -19,6 +19,7 @@ import '../setting/setting_listener.dart';
import '../setting/setting_service.dart';
import '../sort/sort_listener.dart';
import '../sort/sort_service.dart';
import 'field_info.dart';
import 'field_listener.dart';
class _GridFieldNotifier extends ChangeNotifier {
@@ -438,12 +439,18 @@ class FieldController {
}
void _updateFieldInfos() {
final List<FieldInfo> newFieldInfos = [];
for (final field in _fieldNotifier.fieldInfos) {
field._isGroupField = _groupConfigurationByFieldId[field.id] != null;
field._hasFilter = _filterPBByFieldId[field.id] != null;
field._hasSort = _sortPBByFieldId[field.id] != null;
newFieldInfos.add(
field.copyWith(
isGroupField: _groupConfigurationByFieldId[field.id] != null,
hasFilter: _filterPBByFieldId[field.id] != null,
hasSort: _sortPBByFieldId[field.id] != null,
),
);
}
_fieldNotifier.notify();
_fieldNotifier.fieldInfos = newFieldInfos;
}
Future<void> dispose() async {
@@ -488,7 +495,7 @@ class FieldController {
}
_fieldNotifier.fieldInfos =
newFields.map((field) => FieldInfo(field: field)).toList();
newFields.map((field) => FieldInfo.initial(field)).toList();
_loadFilters();
_loadSorts();
_updateFieldInfos();
@@ -652,7 +659,7 @@ class FieldController {
}
final List<FieldInfo> newFieldInfos = fieldInfos;
for (final indexField in insertedFields) {
final fieldInfo = FieldInfo(field: indexField.field_1);
final fieldInfo = FieldInfo.initial(indexField.field_1);
if (newFieldInfos.length > indexField.index) {
newFieldInfos.insert(indexField.index, fieldInfo);
} else {
@@ -674,7 +681,7 @@ class FieldController {
newFields.indexWhere((field) => field.id == updatedFieldPB.id);
if (index != -1) {
newFields.removeAt(index);
final fieldInfo = FieldInfo(field: updatedFieldPB);
final fieldInfo = FieldInfo.initial(updatedFieldPB);
newFields.insert(index, fieldInfo);
updatedFields.add(fieldInfo);
}
@@ -693,7 +700,7 @@ class RowCacheDependenciesImpl extends RowFieldsDelegate with RowLifeCycle {
RowCacheDependenciesImpl(FieldController cache) : _fieldController = cache;
@override
UnmodifiableListView<FieldInfo> get fields =>
UnmodifiableListView<FieldInfo> get fieldInfos =>
UnmodifiableListView(_fieldController.fieldInfos);
@override
@@ -729,72 +736,3 @@ FieldInfo? _findFieldInfo({
return null;
}
}
class FieldInfo {
final FieldPB _field;
bool _isGroupField = false;
bool _hasFilter = false;
bool _hasSort = 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 canBeGroup {
switch (_field.fieldType) {
case FieldType.URL:
case FieldType.Checkbox:
case FieldType.MultiSelect:
case FieldType.SingleSelect:
return true;
default:
return false;
}
}
bool get canCreateFilter {
if (hasFilter) return false;
switch (_field.fieldType) {
case FieldType.Checkbox:
case FieldType.MultiSelect:
case FieldType.RichText:
case FieldType.SingleSelect:
case FieldType.Checklist:
return true;
default:
return false;
}
}
bool get canCreateSort {
if (_hasSort) return false;
switch (_field.fieldType) {
case FieldType.RichText:
case FieldType.Checkbox:
case FieldType.Number:
return true;
default:
return false;
}
}
FieldInfo({required FieldPB field}) : _field = field;
}
@@ -0,0 +1,71 @@
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'field_info.freezed.dart';
@freezed
class FieldInfo with _$FieldInfo {
const FieldInfo._();
const factory FieldInfo({
required FieldPB field,
required bool isGroupField,
required bool hasFilter,
required bool hasSort,
}) = _FieldInfo;
String get id => field.id;
FieldType get fieldType => field.fieldType;
String get name => field.name;
factory FieldInfo.initial(FieldPB field) => FieldInfo(
field: field,
hasFilter: false,
hasSort: false,
isGroupField: false,
);
bool get canBeGroup {
switch (field.fieldType) {
case FieldType.URL:
case FieldType.Checkbox:
case FieldType.MultiSelect:
case FieldType.SingleSelect:
return true;
default:
return false;
}
}
bool get canCreateFilter {
if (hasFilter) return false;
switch (field.fieldType) {
case FieldType.Checkbox:
case FieldType.MultiSelect:
case FieldType.RichText:
case FieldType.SingleSelect:
case FieldType.Checklist:
return true;
default:
return false;
}
}
bool get canCreateSort {
if (hasSort) return false;
switch (field.fieldType) {
case FieldType.RichText:
case FieldType.Checkbox:
case FieldType.Number:
case FieldType.DateTime:
case FieldType.SingleSelect:
case FieldType.MultiSelect:
return true;
default:
return false;
}
}
}
@@ -1,4 +1,3 @@
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:flowy_infra/notifier.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
@@ -1,7 +1,6 @@
import 'package:appflowy/plugins/database_view/application/field/field_listener.dart';
import 'package:appflowy/plugins/database_view/application/field/field_service.dart';
import 'package:appflowy/plugins/database_view/application/row/row_service.dart';
import 'package:appflowy/workspace/application/view/prelude.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/row_entities.pb.dart';
@@ -115,18 +114,6 @@ class RowBannerBloc extends Bloc<RowBannerEvent, RowBannerState> {
(err) => Log.error(err),
);
});
// Set the icon and cover of the view
ViewBackendService.updateView(
viewId: viewId,
iconURL: iconURL,
coverURL: coverURL,
).then((result) {
result.fold(
(l) => null,
(err) => Log.error(err),
);
});
}
}
@@ -136,7 +123,7 @@ class RowBannerEvent with _$RowBannerEvent {
const factory RowBannerEvent.didReceiveRowMeta(RowMetaPB rowMeta) =
_DidReceiveRowMeta;
const factory RowBannerEvent.didReceiveFieldUpdate(FieldPB field) =
_DidReceiveFieldUdate;
_DidReceiveFieldUpdate;
const factory RowBannerEvent.setIcon(String iconURL) = _SetIcon;
const factory RowBannerEvent.setCover(String coverURL) = _SetCover;
}
@@ -1,4 +1,5 @@
import 'dart:collection';
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
@@ -6,7 +7,6 @@ import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import '../cell/cell_service.dart';
import '../field/field_controller.dart';
import 'row_list.dart';
import 'row_service.dart';
part 'row_cache.freezed.dart';
@@ -15,7 +15,7 @@ typedef RowUpdateCallback = void Function();
/// A delegate that provides the fields of the row.
abstract class RowFieldsDelegate {
UnmodifiableListView<FieldInfo> get fields;
UnmodifiableListView<FieldInfo> get fieldInfos;
void onFieldsChanged(void Function(List<FieldInfo>) callback);
}
@@ -244,12 +244,12 @@ class RowCache {
CellContextByFieldId _makeCells(RowMetaPB rowMeta) {
// ignore: prefer_collection_literals
final cellContextMap = CellContextByFieldId();
for (final field in _fieldDelegate.fields) {
if (field.visibility) {
cellContextMap[field.id] = DatabaseCellContext(
for (final fieldInfo in _fieldDelegate.fieldInfos) {
if (fieldInfo.field.visibility) {
cellContextMap[fieldInfo.id] = DatabaseCellContext(
rowMeta: rowMeta,
viewId: viewId,
fieldInfo: field,
fieldInfo: fieldInfo,
);
}
}
@@ -259,7 +259,7 @@ class RowCache {
RowInfo buildGridRow(RowMetaPB rowMetaPB) {
return RowInfo(
viewId: viewId,
fields: _fieldDelegate.fields,
fields: _fieldDelegate.fieldInfos,
rowId: rowMetaPB.id,
rowMeta: rowMetaPB,
);
@@ -1,4 +1,5 @@
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -26,8 +27,8 @@ class DatabaseGroupBloc extends Bloc<DatabaseGroupEvent, DatabaseGroupState> {
initial: () {
_startListening();
},
didReceiveFieldUpdate: (fieldContexts) {
emit(state.copyWith(fieldContexts: fieldContexts));
didReceiveFieldUpdate: (fieldInfos) {
emit(state.copyWith(fieldInfos: fieldInfos));
},
setGroupByField: (String fieldId, FieldType fieldType) async {
final result = await _groupBackendSvc.groupByField(
@@ -50,8 +51,8 @@ class DatabaseGroupBloc extends Bloc<DatabaseGroupEvent, DatabaseGroupState> {
}
void _startListening() {
_onFieldsFn = (fieldContexts) =>
add(DatabaseGroupEvent.didReceiveFieldUpdate(fieldContexts));
_onFieldsFn = (fieldInfos) =>
add(DatabaseGroupEvent.didReceiveFieldUpdate(fieldInfos));
_fieldController.addListener(
onReceiveFields: _onFieldsFn,
listenWhen: () => !isClosed,
@@ -75,15 +76,15 @@ class DatabaseGroupEvent with _$DatabaseGroupEvent {
class DatabaseGroupState with _$DatabaseGroupState {
const factory DatabaseGroupState({
required String viewId,
required List<FieldInfo> fieldContexts,
required List<FieldInfo> fieldInfos,
}) = _DatabaseGroupState;
factory DatabaseGroupState.initial(
String viewId,
List<FieldInfo> fieldContexts,
List<FieldInfo> fieldInfos,
) =>
DatabaseGroupState(
viewId: viewId,
fieldContexts: fieldContexts,
fieldInfos: fieldInfos,
);
}
@@ -1,4 +1,5 @@
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'package:appflowy_backend/log.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -1,6 +1,8 @@
import 'dart:async';
import 'dart:collection';
import 'package:appflowy/plugins/database_view/application/defines.dart';
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'package:appflowy/plugins/database_view/application/row/row_service.dart';
import 'package:appflowy_board/appflowy_board.dart';
import 'package:dartz/dartz.dart';
@@ -254,11 +256,14 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
Future<void> _openGrid(Emitter<BoardState> emit) async {
final result = await databaseController.open();
result.fold(
(grid) => emit(
state.copyWith(loadingState: GridLoadingState.finish(left(unit))),
),
(grid) {
databaseController.setIsLoading(false);
emit(
state.copyWith(loadingState: LoadingState.finish(left(unit))),
);
},
(err) => emit(
state.copyWith(loadingState: GridLoadingState.finish(right(err))),
state.copyWith(loadingState: LoadingState.finish(right(err))),
),
);
}
@@ -323,7 +328,7 @@ class BoardState with _$BoardState {
required Option<DatabasePB> grid,
required List<String> groupIds,
required Option<BoardEditingRow> editingRow,
required GridLoadingState loadingState,
required LoadingState loadingState,
required Option<FlowyError> noneOrError,
}) = _BoardState;
@@ -333,18 +338,10 @@ class BoardState with _$BoardState {
groupIds: [],
editingRow: none(),
noneOrError: none(),
loadingState: const _Loading(),
loadingState: const LoadingState.loading(),
);
}
@freezed
class GridLoadingState with _$GridLoadingState {
const factory GridLoadingState.loading() = _Loading;
const factory GridLoadingState.finish(
Either<Unit, FlowyError> successOrFail,
) = _Finish;
}
class GridFieldEquatable extends Equatable {
final UnmodifiableListView<FieldPB> _fields;
const GridFieldEquatable(
@@ -35,6 +35,7 @@ class BoardPageTabBarBuilderImpl implements DatabaseTabBarItemBuilder {
BuildContext context,
ViewPB view,
DatabaseController controller,
bool shrinkWrap,
) {
return BoardPage(
key: _makeValueKey(controller),
@@ -298,6 +299,7 @@ class _BoardContentState extends State<BoardContent> {
rowCache: rowCache,
cardData: groupData.group.groupId,
groupingFieldId: groupItem.fieldInfo.id,
groupId: groupData.group.groupId,
isEditing: isEditing,
cellBuilder: cellBuilder,
renderHook: renderHook,
@@ -12,10 +12,10 @@ class BoardSettingBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SizedBox(
height: 40,
height: 20,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
const Spacer(),
SettingButton(databaseController: databaseController),
],
),
@@ -1,5 +1,7 @@
import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart';
import 'package:appflowy/plugins/database_view/application/defines.dart';
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'package:appflowy/plugins/database_view/application/row/row_service.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/log.dart';
@@ -133,11 +135,14 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
Future<void> _openDatabase(Emitter<CalendarState> emit) async {
final result = await databaseController.open();
result.fold(
(database) => emit(
state.copyWith(loadingState: DatabaseLoadingState.finish(left(unit))),
),
(database) {
databaseController.setIsLoading(false);
emit(
state.copyWith(loadingState: LoadingState.finish(left(unit))),
);
},
(err) => emit(
state.copyWith(loadingState: DatabaseLoadingState.finish(right(err))),
state.copyWith(loadingState: LoadingState.finish(right(err))),
),
);
}
@@ -425,7 +430,7 @@ class CalendarState with _$CalendarState {
CalendarEventData<CalendarDayEvent>? updateEvent,
required List<String> deleteEventIds,
required Option<CalendarLayoutSettingPB> settings,
required DatabaseLoadingState loadingState,
required LoadingState loadingState,
required Option<FlowyError> noneOrError,
}) = _CalendarState;
@@ -436,18 +441,10 @@ class CalendarState with _$CalendarState {
deleteEventIds: [],
settings: none(),
noneOrError: none(),
loadingState: const _Loading(),
loadingState: const LoadingState.loading(),
);
}
@freezed
class DatabaseLoadingState with _$DatabaseLoadingState {
const factory DatabaseLoadingState.loading() = _Loading;
const factory DatabaseLoadingState.finish(
Either<Unit, FlowyError> successOrFail,
) = _Finish;
}
class CalendarEditingRow {
RowPB row;
int? index;
@@ -1,5 +1,6 @@
import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart';
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'package:appflowy/plugins/database_view/application/row/row_service.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/log.dart';
@@ -254,18 +254,23 @@ class _EventList extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Flexible(
child: ListView.separated(
itemBuilder: (BuildContext context, int index) => EventCard(
event: events[index],
viewId: viewId,
rowCache: rowCache,
constraints: constraints,
child: ScrollConfiguration(
behavior: ScrollConfiguration.of(context).copyWith(
scrollbars: true,
),
child: ListView.separated(
itemBuilder: (BuildContext context, int index) => EventCard(
event: events[index],
viewId: viewId,
rowCache: rowCache,
constraints: constraints,
),
itemCount: events.length,
padding: const EdgeInsets.fromLTRB(8.0, 0, 8.0, 8.0),
separatorBuilder: (BuildContext context, int index) =>
VSpace(GridSize.typeOptionSeparatorHeight),
shrinkWrap: true,
),
itemCount: events.length,
padding: const EdgeInsets.fromLTRB(8.0, 0, 8.0, 8.0),
separatorBuilder: (BuildContext context, int index) =>
VSpace(GridSize.typeOptionSeparatorHeight),
shrinkWrap: true,
),
);
}
@@ -1,7 +1,6 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
import 'package:appflowy/plugins/database_view/calendar/application/calendar_bloc.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
import 'package:appflowy/plugins/database_view/tar_bar/tab_bar_view.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/calendar_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
@@ -27,11 +26,13 @@ class CalendarPageTabBarBuilderImpl implements DatabaseTabBarItemBuilder {
BuildContext context,
ViewPB view,
DatabaseController controller,
bool shrinkWrap,
) {
return CalendarPage(
key: _makeValueKey(controller),
view: view,
databaseController: controller,
shrinkWrap: shrinkWrap,
);
}
@@ -59,9 +60,11 @@ class CalendarPageTabBarBuilderImpl implements DatabaseTabBarItemBuilder {
class CalendarPage extends StatefulWidget {
final ViewPB view;
final DatabaseController databaseController;
final bool shrinkWrap;
const CalendarPage({
required this.view,
required this.databaseController,
this.shrinkWrap = false,
super.key,
});
@@ -95,12 +98,8 @@ class _CalendarPageState extends State<CalendarPage> {
Widget build(BuildContext context) {
return CalendarControllerProvider(
controller: _eventController,
child: MultiBlocProvider(
providers: [
BlocProvider<CalendarBloc>.value(
value: _calendarBloc,
)
],
child: BlocProvider<CalendarBloc>.value(
value: _calendarBloc,
child: MultiBlocListener(
listeners: [
BlocListener<CalendarBloc, CalendarState>(
@@ -159,14 +158,21 @@ class _CalendarPageState extends State<CalendarPage> {
],
child: BlocBuilder<CalendarBloc, CalendarState>(
builder: (context, state) {
return Column(
children: [
_buildCalendar(
return ValueListenableBuilder<bool>(
valueListenable: widget.databaseController.isLoading,
builder: (_, value, ___) {
if (value) {
return const Center(
child: CircularProgressIndicator.adaptive(),
);
}
return _buildCalendar(
context,
_eventController,
state.settings
.foldLeft(0, (previous, a) => a.firstDayOfWeek),
),
],
);
},
);
},
),
@@ -175,19 +181,29 @@ class _CalendarPageState extends State<CalendarPage> {
);
}
Widget _buildCalendar(EventController eventController, int firstDayOfWeek) {
return Expanded(
child: Padding(
padding: GridSize.contentInsets,
child: MonthView(
key: _calendarState,
controller: _eventController,
cellAspectRatio: .6,
startDay: _weekdayFromInt(firstDayOfWeek),
borderColor: Theme.of(context).dividerColor,
headerBuilder: _headerNavigatorBuilder,
weekDayBuilder: _headerWeekDayBuilder,
cellBuilder: _calendarDayBuilder,
Widget _buildCalendar(
BuildContext context,
EventController eventController,
int firstDayOfWeek,
) {
return Padding(
padding: CalendarSize.contentInsets,
child: LayoutBuilder(
// must specify MonthView width for useAvailableVerticalSpace to work properly
builder: (context, constraints) => ScrollConfiguration(
behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
child: MonthView(
key: _calendarState,
controller: _eventController,
width: constraints.maxWidth,
cellAspectRatio: 0.6,
startDay: _weekdayFromInt(firstDayOfWeek),
borderColor: Theme.of(context).dividerColor,
headerBuilder: _headerNavigatorBuilder,
weekDayBuilder: _headerWeekDayBuilder,
cellBuilder: _calendarDayBuilder,
useAvailableVerticalSpace: widget.shrinkWrap,
),
),
),
);
@@ -1,9 +1,19 @@
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
import 'package:flutter/widgets.dart';
class CalendarSize {
static double scale = 1;
static double get scrollBarSize => 12 * scale;
static double get headerContainerPadding => 12 * scale;
static EdgeInsets get contentInsets => EdgeInsets.fromLTRB(
GridSize.leadingHeaderPadding,
CalendarSize.headerContainerPadding,
GridSize.leadingHeaderPadding,
CalendarSize.headerContainerPadding,
);
static double get scrollBarSize => 8 * scale;
static double get navigatorButtonWidth => 20 * scale;
static double get navigatorButtonHeight => 25 * scale;
static EdgeInsets get daysOfWeekInsets =>
@@ -8,6 +8,7 @@ import 'package:appflowy/plugins/database_view/widgets/setting/setting_button.da
import 'package:appflowy_backend/protobuf/flowy-database2/calendar_entities.pb.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
@@ -23,11 +24,12 @@ class CalendarSettingBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SizedBox(
height: 40,
height: 20,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
UnscheduleEventsButton(databaseController: databaseController),
const HSpace(2),
SettingButton(
databaseController: databaseController,
),
@@ -82,9 +84,14 @@ class _UnscheduleEventsButtonState extends State<UnscheduleEventsButton> {
builder: (context, state) {
return FlowyTextButton(
"${LocaleKeys.calendar_settings_noDateTitle.tr()} (${state.unscheduleEvents.length})",
fontSize: FontSizes.s11,
fontColor: AFThemeExtension.of(context).textColor,
fontWeight: FontWeight.w400,
fillColor: Colors.transparent,
hoverColor: AFThemeExtension.of(context).lightGreyHover,
padding: GridSize.typeOptionContentInsets,
padding: GridSize.toolbarSettingButtonInsets,
radius: Corners.s4Border,
onPressed: () => _popoverController.show(),
);
},
),
@@ -1,4 +1,5 @@
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'package:appflowy/plugins/database_view/application/filter/filter_service.dart';
import 'package:dartz/dartz.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pbserver.dart';
@@ -1,4 +1,5 @@
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/filter_info.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -1,16 +1,16 @@
import 'dart:async';
import 'package:appflowy/plugins/database_view/application/defines.dart';
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
import 'package:appflowy/plugins/database_view/application/row/row_service.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/filter_info.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/sort_info.dart';
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import '../../application/field/field_controller.dart';
import '../../application/database_controller.dart';
import 'dart:collection';
@@ -134,12 +134,13 @@ class GridBloc extends Bloc<GridEvent, GridState> {
final result = await databaseController.open();
result.fold(
(grid) {
databaseController.setIsLoading(false);
emit(
state.copyWith(loadingState: GridLoadingState.finish(left(unit))),
state.copyWith(loadingState: LoadingState.finish(left(unit))),
);
},
(err) => emit(
state.copyWith(loadingState: GridLoadingState.finish(right(err))),
state.copyWith(loadingState: LoadingState.finish(right(err))),
),
);
}
@@ -177,7 +178,7 @@ class GridState with _$GridState {
required GridFieldEquatable fields,
required List<RowInfo> rowInfos,
required int rowCount,
required GridLoadingState loadingState,
required LoadingState loadingState,
required bool reorderable,
required ChangedReason reason,
required List<SortInfo> sorts,
@@ -191,40 +192,33 @@ class GridState with _$GridState {
grid: none(),
viewId: viewId,
reorderable: true,
loadingState: const _Loading(),
loadingState: const LoadingState.loading(),
reason: const InitialListState(),
filters: [],
sorts: [],
);
}
@freezed
class GridLoadingState with _$GridLoadingState {
const factory GridLoadingState.loading() = _Loading;
const factory GridLoadingState.finish(
Either<Unit, FlowyError> successOrFail,
) = _Finish;
}
class GridFieldEquatable extends Equatable {
final List<FieldInfo> _fields;
final List<FieldInfo> _fieldInfos;
const GridFieldEquatable(
List<FieldInfo> fields,
) : _fields = fields;
List<FieldInfo> fieldInfos,
) : _fieldInfos = fieldInfos;
@override
List<Object?> get props {
if (_fields.isEmpty) {
if (_fieldInfos.isEmpty) {
return [];
}
return [
_fields.length,
_fields
.map((field) => field.width)
_fieldInfos.length,
_fieldInfos
.map((fieldInfo) => fieldInfo.field.width)
.reduce((value, element) => value + element),
];
}
UnmodifiableListView<FieldInfo> get value => UnmodifiableListView(_fields);
UnmodifiableListView<FieldInfo> get value =>
UnmodifiableListView(_fieldInfos);
}
@@ -1,4 +1,5 @@
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -26,7 +27,7 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
emit(
state.copyWith(
fields: value.fields
.where((element) => element.visibility)
.where((element) => element.field.visibility)
.toList(),
),
);
@@ -15,6 +15,7 @@ class RowActionSheetBloc
RowActionSheetBloc({
required String viewId,
required RowId rowId,
String? groupId,
}) : _rowService = RowBackendService(viewId: viewId),
super(RowActionSheetState.initial(rowId)) {
on<RowActionSheetEvent>(
@@ -25,7 +26,10 @@ class RowActionSheetBloc
logResult(result);
},
duplicateRow: () async {
final result = await _rowService.duplicateRow(rowId: state.rowId);
final result = await _rowService.duplicateRow(
rowId: state.rowId,
groupId: groupId,
);
logResult(result);
},
);
@@ -1,4 +1,5 @@
import 'dart:collection';
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'package:appflowy/plugins/database_view/application/row/row_listener.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:equatable/equatable.dart';
@@ -7,7 +8,6 @@ import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
import '../../../application/cell/cell_service.dart';
import '../../../application/field/field_controller.dart';
import '../../../application/row/row_cache.dart';
import '../../../application/row/row_controller.dart';
import '../../../application/row/row_service.dart';
@@ -119,16 +119,16 @@ class RowState with _$RowState {
}
class GridCellEquatable extends Equatable {
final FieldInfo _fieldContext;
final FieldInfo _fieldInfo;
const GridCellEquatable(FieldInfo field) : _fieldContext = field;
const GridCellEquatable(FieldInfo field) : _fieldInfo = field;
@override
List<Object?> get props => [
_fieldContext.id,
_fieldContext.fieldType,
_fieldContext.visibility,
_fieldContext.width,
_fieldInfo.id,
_fieldInfo.fieldType,
_fieldInfo.field.visibility,
_fieldInfo.field.width,
];
}
@@ -1,3 +1,4 @@
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'package:dartz/dartz.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pbserver.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/sort_entities.pbenum.dart';
@@ -1,4 +1,5 @@
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'package:appflowy/plugins/database_view/application/sort/sort_service.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/sort_info.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/sort_entities.pbenum.dart';
@@ -1,3 +1,4 @@
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
@@ -1,4 +1,4 @@
import '../../../application/field/field_controller.dart';
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
List<FieldInfo> getCreatableSorts(List<FieldInfo> fieldInfos) {
final List<FieldInfo> creatableFields = List.from(fieldInfos);
@@ -50,6 +50,7 @@ class GridPageTabBarBuilderImpl implements DatabaseTabBarItemBuilder {
BuildContext context,
ViewPB view,
DatabaseController controller,
bool shrinkWrap,
) {
return GridPage(
key: _makeValueKey(controller),
@@ -1,4 +1,4 @@
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'sizes.dart';
class GridLayout {
@@ -6,7 +6,7 @@ class GridLayout {
if (fields.isEmpty) return 0;
final fieldsWidth = fields
.map((field) => field.width.toDouble())
.map((fieldInfo) => fieldInfo.field.width.toDouble())
.reduce((value, element) => value + element);
return fieldsWidth +
@@ -3,7 +3,7 @@ import 'package:flutter/widgets.dart';
class GridSize {
static double scale = 1;
static double get scrollBarSize => 12 * scale;
static double get scrollBarSize => 8 * scale;
static double get headerHeight => 40 * scale;
static double get footerHeight => 40 * scale;
static double get leadingHeaderPadding => 50 * scale;
@@ -30,6 +30,9 @@ class GridSize {
static EdgeInsets get typeOptionContentInsets => const EdgeInsets.all(4);
static EdgeInsets get toolbarSettingButtonInsets =>
const EdgeInsets.symmetric(horizontal: 8, vertical: 2);
static EdgeInsets get footerContentInsets => EdgeInsets.fromLTRB(
GridSize.leadingHeaderPadding,
GridSize.headerContainerPadding,
@@ -107,7 +107,12 @@ class _CheckboxFilterEditorState extends State<CheckboxFilterEditor> {
height: 20,
child: Row(
children: [
FlowyText(state.filterInfo.fieldInfo.name),
Expanded(
child: FlowyText(
state.filterInfo.fieldInfo.field.name,
overflow: TextOverflow.ellipsis,
),
),
const HSpace(4),
CheckboxFilterConditionList(
filterInfo: state.filterInfo,
@@ -118,7 +123,6 @@ class _CheckboxFilterEditorState extends State<CheckboxFilterEditor> {
.add(CheckboxFilterEditorEvent.updateCondition(condition));
},
),
const Spacer(),
DisclosureButton(
popoverMutex: popoverMutex,
onAction: (action) {
@@ -91,12 +91,16 @@ class ChecklistState extends State<ChecklistFilterEditor> {
height: 20,
child: Row(
children: [
FlowyText(state.filterInfo.fieldInfo.name),
Expanded(
child: FlowyText(
state.filterInfo.fieldInfo.field.name,
overflow: TextOverflow.ellipsis,
),
),
const HSpace(4),
ChecklistFilterConditionList(
filterInfo: state.filterInfo,
),
const Spacer(),
DisclosureButton(
popoverMutex: widget.popoverMutex,
onAction: (action) {
@@ -39,7 +39,7 @@ class ChoiceChipButton extends StatelessWidget {
decoration: decoration,
useIntrinsicWidth: true,
text: FlowyText(
filterInfo.fieldInfo.name,
filterInfo.fieldInfo.field.name,
color: AFThemeExtension.of(context).textColor,
),
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
@@ -142,7 +142,12 @@ class _SelectOptionFilterEditorState extends State<SelectOptionFilterEditor> {
height: 20,
child: Row(
children: [
FlowyText(state.filterInfo.fieldInfo.name),
Expanded(
child: FlowyText(
state.filterInfo.fieldInfo.field.name,
overflow: TextOverflow.ellipsis,
),
),
const HSpace(4),
SelectOptionFilterConditionList(
filterInfo: state.filterInfo,
@@ -153,7 +158,6 @@ class _SelectOptionFilterEditorState extends State<SelectOptionFilterEditor> {
);
},
),
const Spacer(),
DisclosureButton(
popoverMutex: popoverMutex,
onAction: (action) {
@@ -1,4 +1,5 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_extension.dart';
import 'package:easy_localization/easy_localization.dart';
@@ -161,7 +162,7 @@ class GridFilterPropertyCell extends StatelessWidget {
return FlowyButton(
hoverColor: AFThemeExtension.of(context).lightGreyHover,
text: FlowyText.medium(
fieldInfo.name,
fieldInfo.field.name,
color: AFThemeExtension.of(context).textColor,
),
onTap: () => onTap(fieldInfo),
@@ -1,4 +1,4 @@
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/checkbox_filter.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/checklist_filter.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/date_filter.pb.dart';
@@ -96,7 +96,7 @@ class _GridHeaderState extends State<_GridHeader> {
buildWhen: (previous, current) => previous.fields != current.fields,
builder: (context, state) {
final cells = state.fields
.where((field) => field.visibility)
.where((fieldInfo) => fieldInfo.field.visibility)
.map(
(field) => FieldContext(
viewId: widget.viewId,
@@ -1,6 +1,6 @@
import 'dart:typed_data';
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy/plugins/database_view/application/field/field_info.dart';
import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_context.dart';
import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_data_controller.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/checkbox_entities.pb.dart';
Loaded 100 of 654 files, more files were not shown because too many files have changed in this diff. Show more