Compare commits
197
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12198dddb7 | ||
|
|
5efdd6e8b9 | ||
|
|
d1c867d089 | ||
|
|
22b108df70 | ||
|
|
aff9a61919 | ||
|
|
043cd3f3bb | ||
|
|
29fb4af40a | ||
|
|
a2e211555e | ||
|
|
d1af172fb7 | ||
|
|
23b6f94e82 | ||
|
|
82fffba45a | ||
|
|
0373088fb8 | ||
|
|
0da0f320b7 | ||
|
|
4abdcd9478 | ||
|
|
1e54c2dc7c | ||
|
|
01747f13e8 | ||
|
|
4a5eda6eeb | ||
|
|
b5d799655a | ||
|
|
43b250ec28 | ||
|
|
20e82880ff | ||
|
|
661a0879c6 | ||
|
|
2fb18dd051 | ||
|
|
6334255e15 | ||
|
|
f66821715f | ||
|
|
620e027c3e | ||
|
|
864768b3ba | ||
|
|
a8b4f22703 | ||
|
|
432db0f6d5 | ||
|
|
e8e4162a5c | ||
|
|
241234726f | ||
|
|
e426970eed | ||
|
|
d5544af6c5 | ||
|
|
9361afd573 | ||
|
|
dc93a336d6 | ||
|
|
4e5482488f | ||
|
|
5bbf174ffd | ||
|
|
f36e3ae378 | ||
|
|
dc6349b4b1 | ||
|
|
44d8def3ce | ||
|
|
c6ad57f11d | ||
|
|
ff23165d3e | ||
|
|
253e7597c4 | ||
|
|
d2e3fdfefd | ||
|
|
a2336f5bf7 | ||
|
|
8833df1740 | ||
|
|
1524b10a8a | ||
|
|
fe0fa9b530 | ||
|
|
80afcf44c0 | ||
|
|
e500c89978 | ||
|
|
079b9888a8 | ||
|
|
0fe383e538 | ||
|
|
c006e29afc | ||
|
|
655a2b7093 | ||
|
|
23c67bcdba | ||
|
|
521fffd97c | ||
|
|
2ecc2a67a9 | ||
|
|
2d11215bb2 | ||
|
|
6d0c9f766b | ||
|
|
7a3f013a87 | ||
|
|
8c1520b273 | ||
|
|
a7b850e752 | ||
|
|
42dc6017c6 | ||
|
|
f9fba6b561 | ||
|
|
46896b5a2b | ||
|
|
2b8dca209e | ||
|
|
c78f23e1c0 | ||
|
|
50f5be3e75 | ||
|
|
e1c68c1b72 | ||
|
|
3bcadff152 | ||
|
|
b09ff040f1 | ||
|
|
bf087bba5d | ||
|
|
605a53f2ae | ||
|
|
ecc5555a4e | ||
|
|
d2a5bab6d9 | ||
|
|
a1dec0f269 | ||
|
|
b24fbc6b60 | ||
|
|
bbb3f95a13 | ||
|
|
7859fc7922 | ||
|
|
f812040f04 | ||
|
|
7d96c2dfd4 | ||
|
|
e4132ed217 | ||
|
|
a7f40b2adc | ||
|
|
c996c9c28e | ||
|
|
f59b8a2e5c | ||
|
|
f7480cba16 | ||
|
|
271b250eac | ||
|
|
6b1e7b6ac8 | ||
|
|
bf2a00b133 | ||
|
|
a8ed93054c | ||
|
|
b9ad2768cf | ||
|
|
54c9d12171 | ||
|
|
40312f4260 | ||
|
|
d2ca41c8f5 | ||
|
|
7586a0ed48 | ||
|
|
d8b32168a9 | ||
|
|
8ca541bfa1 | ||
|
|
b64da2c02f | ||
|
|
fdaca36b87 | ||
|
|
8557383a89 | ||
|
|
fa86480458 | ||
|
|
d75eb05707 | ||
|
|
a2b32585a1 | ||
|
|
75cea400d2 | ||
|
|
ed82ec8eef | ||
|
|
b6debd6c71 | ||
|
|
a05ed63a3e | ||
|
|
3e75f1f24a | ||
|
|
e607694729 | ||
|
|
91d870415e | ||
|
|
21a497924c | ||
|
|
4a126e17ce | ||
|
|
8bf97ad5c6 | ||
|
|
a85d11fb6d | ||
|
|
1fd34bc58a | ||
|
|
70a7b856a2 | ||
|
|
49683e6079 | ||
|
|
13107b63e9 | ||
|
|
ce1a6e4fca | ||
|
|
1d23e28eaf | ||
|
|
2a4d78b688 | ||
|
|
cbe452a73d | ||
|
|
27899ee993 | ||
|
|
785597f53e | ||
|
|
e2ce274718 | ||
|
|
6be9c00127 | ||
|
|
aa621a8d84 | ||
|
|
2d4300e931 | ||
|
|
dc12938ab6 | ||
|
|
2d674060c6 | ||
|
|
ecca81f3b8 | ||
|
|
15329b39d0 | ||
|
|
bd5f5f8b9e | ||
|
|
4f4be7eac7 | ||
|
|
f1a4deb459 | ||
|
|
4708c0f779 | ||
|
|
3d7a500550 | ||
|
|
815c99710e | ||
|
|
c9fe93920f | ||
|
|
5012b99c9b | ||
|
|
6b46372aa0 | ||
|
|
b6d873db1b | ||
|
|
38d2bd7ee8 | ||
|
|
bb3e9d5bd8 | ||
|
|
94060a0a99 | ||
|
|
86696b271e | ||
|
|
76fde00cc4 | ||
|
|
b794f3894e | ||
|
|
3b72f90ca5 | ||
|
|
d73e388d01 | ||
|
|
383e06ff2d | ||
|
|
ff93fbf0e6 | ||
|
|
bd7977d8ba | ||
|
|
6e7d044208 | ||
|
|
d8ca693976 | ||
|
|
52e7fb49cd | ||
|
|
f6e33a59d5 | ||
|
|
a0e981ec6b | ||
|
|
041e3c155a | ||
|
|
e4eff7e632 | ||
|
|
9f66dcdc8f | ||
|
|
890ac5a213 | ||
|
|
1cecfae6f4 | ||
|
|
1757570337 | ||
|
|
57d4652824 | ||
|
|
1e485188eb | ||
|
|
03e8dba5f3 | ||
|
|
d5cfd054cc | ||
|
|
9c9168ac5b | ||
|
|
4762d69851 | ||
|
|
aec7bc847e | ||
|
|
4d42c9ea68 | ||
|
|
0d64aa4311 | ||
|
|
2a59579c17 | ||
|
|
4ad7c48b25 | ||
|
|
2c0cdfa6c5 | ||
|
|
8b04506ac4 | ||
|
|
a1395cb54a | ||
|
|
526de2eb5c | ||
|
|
e40e1e9a8a | ||
|
|
ace729eb78 | ||
|
|
189f0e4b58 | ||
|
|
e8afcbb828 | ||
|
|
42e83b3ce9 | ||
|
|
859eaf903b | ||
|
|
0bfe071caf | ||
|
|
b8b7a10b33 | ||
|
|
cb44a885a1 | ||
|
|
083be32fa6 | ||
|
|
8b6575d1ee | ||
|
|
a8f136eda2 | ||
|
|
13b3439bd6 | ||
|
|
13a35672ab | ||
|
|
857b3aa106 | ||
|
|
a0139dd475 | ||
|
|
9a5dbbb3ce | ||
|
|
acae34836e | ||
|
|
68c4e19f91 |
No files matched your search
@@ -63,7 +63,7 @@ runs:
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev libmpv-dev mpv
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
vcpkg integrate install
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
|
||||
@@ -52,7 +52,7 @@ runs:
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev network-manager
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev network-manager libmpv-dev mpv
|
||||
shell: bash
|
||||
|
||||
- name: Enable Flutter Desktop
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
# env:
|
||||
# CARGO_TERM_COLOR: always
|
||||
# FLUTTER_VERSION: "3.19.0"
|
||||
# FLUTTER_VERSION: "3.22.0"
|
||||
# RUST_TOOLCHAIN: "1.77.2"
|
||||
# CARGO_MAKE_VERSION: "0.36.6"
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
name: Build Bot
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
dispatch_slash_command:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# get build name from pubspec.yaml
|
||||
- name: Get build version
|
||||
working-directory: frontend/appflowy_flutter
|
||||
id: get_build_name
|
||||
run: |
|
||||
echo "fetching version from pubspec.yaml..."
|
||||
echo "build_name=$(grep 'version: ' pubspec.yaml | awk '{print $2}')" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: peter-evans/slash-command-dispatch@v4
|
||||
with:
|
||||
token: ${{ secrets.PAT }}
|
||||
commands: build
|
||||
static-args: |
|
||||
ref=refs/pull/${{ github.event.issue.number }}/head
|
||||
build_name=${{ steps.get_build_name.outputs.build_name }}
|
||||
@@ -1,72 +0,0 @@
|
||||
name: Deploy Web (Test)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- build/test
|
||||
env:
|
||||
NODE_VERSION: "18.16.0"
|
||||
PNPM_VERSION: "8.5.0"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.WEB_TEST_SSH_PRIVATE_KEY }}
|
||||
REMOTE_HOST: ${{ secrets.WEB_TEST_REMOTE_HOST }}
|
||||
REMOTE_USER: ${{ secrets.WEB_TEST_REMOTE_USER }}
|
||||
SSL_CERTIFICATE: ${{ secrets.WEB_TEST_SSL_CERTIFICATE }}
|
||||
SSL_CERTIFICATE_KEY: ${{ secrets.WEB_TEST_SSL_CERTIFICATE_KEY }}
|
||||
ENV_FILE: test.env
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
- name: setup pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
- name: Node_modules cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: frontend/appflowy_web_app/node_modules
|
||||
key: node-modules-${{ runner.os }}
|
||||
- name: install frontend dependencies
|
||||
working-directory: frontend/appflowy_web_app
|
||||
run: |
|
||||
pnpm install
|
||||
- name: copy env file
|
||||
working-directory: frontend/appflowy_web_app
|
||||
run: |
|
||||
cp ${{ env.ENV_FILE }} .env
|
||||
- name: test and lint
|
||||
working-directory: frontend/appflowy_web_app
|
||||
run: |
|
||||
pnpm run lint
|
||||
- name: build
|
||||
working-directory: frontend/appflowy_web_app
|
||||
run: |
|
||||
pnpm run build
|
||||
- name: generate SSL certificate
|
||||
run: |
|
||||
echo "${{ env.SSL_CERTIFICATE }}" > nginx-signed.crt
|
||||
echo "${{ env.SSL_CERTIFICATE_KEY }}" > nginx-signed.key
|
||||
- name: Deploy to EC2
|
||||
uses: easingthemes/ssh-deploy@main
|
||||
with:
|
||||
SSH_PRIVATE_KEY: ${{ env.SSH_PRIVATE_KEY }}
|
||||
ARGS: "-rlgoDzvc -i"
|
||||
SOURCE: "frontend/appflowy_web_app/dist frontend/appflowy_web_app/Dockerfile frontend/appflowy_web_app/nginx.conf frontend/appflowy_web_app/.env nginx-signed.crt nginx-signed.key"
|
||||
REMOTE_HOST: ${{ env.REMOTE_HOST }}
|
||||
REMOTE_USER: ${{ env.REMOTE_USER }}
|
||||
EXCLUDE: "frontend/appflowy_web_app/dist/, frontend/appflowy_web_app/node_modules/"
|
||||
SCRIPT_AFTER: |
|
||||
docker build -t appflowy-web-app .
|
||||
docker rm -f appflowy-web-app || true
|
||||
docker run -d -p 80:80 -p 443:443 --name appflowy-web-app appflowy-web-app
|
||||
@@ -13,7 +13,7 @@ on:
|
||||
- release/*
|
||||
paths:
|
||||
- frontend/**
|
||||
types: [opened, synchronize, reopened, unlocked, ready_for_review]
|
||||
types: [ opened, synchronize, reopened, unlocked, ready_for_review ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
|
||||
@@ -25,7 +25,7 @@ on:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
FLUTTER_VERSION: "3.19.0"
|
||||
FLUTTER_VERSION: "3.22.0"
|
||||
RUST_TOOLCHAIN: "1.77.2"
|
||||
CARGO_MAKE_VERSION: "0.36.6"
|
||||
|
||||
@@ -89,6 +89,7 @@ jobs:
|
||||
with:
|
||||
os: ${{ matrix.os }}
|
||||
flutter_version: ${{ env.FLUTTER_VERSION }}
|
||||
DISABLE_CI_TEST_LOG: "true"
|
||||
rust_toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
cargo_make_version: ${{ env.CARGO_MAKE_VERSION }}
|
||||
rust_target: ${{ matrix.target }}
|
||||
@@ -172,7 +173,7 @@ jobs:
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev libmpv-dev mpv
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
@@ -202,6 +203,7 @@ jobs:
|
||||
- name: Run Flutter unit tests
|
||||
env:
|
||||
DISABLE_EVENT_LOG: true
|
||||
DISABLE_CI_TEST_LOG: "true"
|
||||
working-directory: frontend
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "macOS" ]; then
|
||||
@@ -246,10 +248,13 @@ jobs:
|
||||
env:
|
||||
BACKEND_VERSION: 0.3.24-amd64
|
||||
run: |
|
||||
docker compose down -v --remove-orphans
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
sleep 10
|
||||
if [ "$(docker ps --filter name=appflowy-cloud -q)" == "" ]; then
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
sleep 10
|
||||
else
|
||||
echo "Docker container 'appflowy-cloud' is already running."
|
||||
fi
|
||||
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
@@ -272,7 +277,7 @@ jobs:
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev libmpv-dev mpv
|
||||
shell: bash
|
||||
|
||||
- name: Enable Flutter Desktop
|
||||
@@ -319,6 +324,12 @@ jobs:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install video dependency
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install libmpv-dev mpv
|
||||
shell: bash
|
||||
|
||||
- name: Flutter Integration Test 1
|
||||
uses: ./.github/actions/flutter_integration_test
|
||||
with:
|
||||
@@ -343,6 +354,12 @@ jobs:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install video dependency
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install libmpv-dev mpv
|
||||
shell: bash
|
||||
|
||||
- name: Flutter Integration Test 2
|
||||
uses: ./.github/actions/flutter_integration_test
|
||||
with:
|
||||
@@ -367,6 +384,12 @@ jobs:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install video dependency
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install libmpv-dev mpv
|
||||
shell: bash
|
||||
|
||||
- name: Flutter Integration Test 3
|
||||
uses: ./.github/actions/flutter_integration_test
|
||||
with:
|
||||
|
||||
@@ -20,7 +20,7 @@ on:
|
||||
- "!frontend/appflowy_web_app/**"
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.19.0"
|
||||
FLUTTER_VERSION: "3.22.0"
|
||||
RUST_TOOLCHAIN: "1.77.2"
|
||||
|
||||
concurrency:
|
||||
@@ -28,20 +28,35 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.event.pull_request.draft != true
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os: [ macos-14 ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
build-self-hosted:
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: self-hosted
|
||||
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Build AppFlowy
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make --profile development-ios-arm64-sim appflowy-core-dev-ios
|
||||
cargo make --profile development-ios-arm64-sim code_generation
|
||||
|
||||
- uses: futureware-tech/simulator-action@v3
|
||||
id: simulator-action
|
||||
with:
|
||||
model: 'iPhone 15'
|
||||
shutdown_after_job: false
|
||||
|
||||
build-macos:
|
||||
if: github.event.pull_request.head.repo.full_name != github.repository
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
@@ -49,8 +64,7 @@ jobs:
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- name: Install flutter
|
||||
id: flutter
|
||||
- name: Install Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
@@ -59,7 +73,7 @@ jobs:
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: ${{ matrix.os }}
|
||||
prefix-key: macos-latest
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ on:
|
||||
- "*"
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.19.0"
|
||||
FLUTTER_VERSION: "3.22.0"
|
||||
RUST_TOOLCHAIN: "1.77.2"
|
||||
|
||||
jobs:
|
||||
@@ -135,7 +135,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job:
|
||||
- { target: x86_64-apple-darwin, os: macos-11, extra-build-args: "" }
|
||||
- { target: x86_64-apple-darwin, os: macos-12, extra-build-args: "" }
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
@@ -233,7 +233,7 @@ jobs:
|
||||
job:
|
||||
- {
|
||||
targets: "aarch64-apple-darwin,x86_64-apple-darwin",
|
||||
os: macos-11,
|
||||
os: macos-latest,
|
||||
extra-build-args: "",
|
||||
}
|
||||
steps:
|
||||
@@ -367,8 +367,8 @@ jobs:
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential libsqlite3-dev libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
|
||||
sudo apt-get install keybinder-3.0 libnotify-dev
|
||||
sudo apt-get -y install alien
|
||||
sudo apt-get install keybinder-3.0
|
||||
sudo apt-get install -y alien libnotify-dev libmpv-dev mpv
|
||||
source $HOME/.cargo/env
|
||||
cargo install --force cargo-make
|
||||
cargo install --force duckscript_cli
|
||||
@@ -479,6 +479,24 @@ jobs:
|
||||
cache-from: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/af_build_cache:buildcache
|
||||
cache-to: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/af_build_cache:buildcache,mode=max
|
||||
|
||||
notify-failure:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build-for-macOS-x86_64
|
||||
- build-for-windows
|
||||
- build-for-linux
|
||||
if: failure()
|
||||
steps:
|
||||
- uses: 8398a7/action-slack@v3
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
text: |
|
||||
🔴🔴🔴Workflow ${{ github.workflow }} in repository ${{ github.repository }} was failed 🔴🔴🔴.
|
||||
fields: repo,message,author,eventName,ref,workflow
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.RELEASE_SLACK_WEBHOOK }}
|
||||
if: always()
|
||||
|
||||
notify-discord:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
|
||||
@@ -8,6 +8,7 @@ on:
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/rust-lib/**"
|
||||
- ".github/workflows/rust_ci.yaml"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
@@ -22,70 +23,25 @@ env:
|
||||
RUST_TOOLCHAIN: "1.77.2"
|
||||
|
||||
jobs:
|
||||
test-on-ubuntu:
|
||||
runs-on: ubuntu-latest
|
||||
self-hosted-job:
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
# - name: Maximize build space
|
||||
# uses: easimon/maximize-build-space@master
|
||||
# with:
|
||||
# root-reserve-mb: 2048
|
||||
# swap-size-mb: 1024
|
||||
# remove-dotnet: 'true'
|
||||
|
||||
# # the following step is required to avoid running out of space
|
||||
# - name: Maximize build space
|
||||
# run: |
|
||||
# sudo rm -rf /usr/share/dotnet
|
||||
# sudo rm -rf /opt/ghc
|
||||
# sudo rm -rf "/usr/local/share/boost"
|
||||
# sudo rm -rf "$AGENT_TOOLSDIRECTORY"
|
||||
# sudo docker image prune --all --force
|
||||
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
profile: minimal
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo install --force cargo-make
|
||||
cargo install --force duckscript_cli
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: "ubuntu-latest"
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
|
||||
- name: Checkout appflowy cloud code
|
||||
- name: Checkout Appflowy Cloud
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: AppFlowy-IO/AppFlowy-Cloud
|
||||
path: AppFlowy-Cloud
|
||||
|
||||
- name: Prepare appflowy cloud env
|
||||
- name: Prepare Appflowy Cloud env
|
||||
working-directory: AppFlowy-Cloud
|
||||
run: |
|
||||
# log level
|
||||
cp deploy.env .env
|
||||
sed -i 's|RUST_LOG=.*|RUST_LOG=trace|' .env
|
||||
sed -i 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env
|
||||
|
||||
- name: Run Docker-Compose
|
||||
working-directory: AppFlowy-Cloud
|
||||
env:
|
||||
BACKEND_VERSION: 0.3.24-amd64
|
||||
run: |
|
||||
docker pull appflowyinc/appflowy_cloud:latest
|
||||
docker compose up -d
|
||||
sed -i '' 's|RUST_LOG=.*|RUST_LOG=trace|' .env
|
||||
sed -i '' 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env
|
||||
|
||||
- name: Run rust-lib tests
|
||||
working-directory: frontend/rust-lib
|
||||
@@ -106,6 +62,84 @@ jobs:
|
||||
run: cargo clippy --all-targets -- -D warnings
|
||||
working-directory: frontend/rust-lib
|
||||
|
||||
ubuntu-job:
|
||||
if: github.event.pull_request.head.repo.full_name != github.repository
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Maximize build space
|
||||
run: |
|
||||
sudo rm -rf /usr/share/dotnet
|
||||
sudo rm -rf /opt/ghc
|
||||
sudo rm -rf "/usr/local/share/boost"
|
||||
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
|
||||
sudo docker image prune --all --force
|
||||
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
profile: minimal
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: ${{ runner.os }}
|
||||
cache-on-failure: true
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
|
||||
- name: Checkout appflowy cloud code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: AppFlowy-IO/AppFlowy-Cloud
|
||||
path: AppFlowy-Cloud
|
||||
|
||||
- name: Prepare appflowy cloud env
|
||||
working-directory: AppFlowy-Cloud
|
||||
run: |
|
||||
cp deploy.env .env
|
||||
sed -i 's|RUST_LOG=.*|RUST_LOG=trace|' .env
|
||||
sed -i 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env
|
||||
|
||||
- name: Run Docker-Compose
|
||||
working-directory: AppFlowy-Cloud
|
||||
run: |
|
||||
if [ "$(docker ps --filter name=appflowy-cloud -q)" == "" ]; then
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
sleep 10
|
||||
else
|
||||
echo "Docker container 'appflowy-cloud' is already running."
|
||||
fi
|
||||
|
||||
- name: Run rust-lib tests
|
||||
working-directory: frontend/rust-lib
|
||||
env:
|
||||
RUST_LOG: info
|
||||
RUST_BACKTRACE: 1
|
||||
af_cloud_test_base_url: http://localhost
|
||||
af_cloud_test_ws_url: ws://localhost/ws/v1
|
||||
af_cloud_test_gotrue_url: http://localhost/gotrue
|
||||
run: |
|
||||
DISABLE_CI_TEST_LOG="true" cargo test --no-default-features --features="dart"
|
||||
|
||||
- name: rustfmt rust-lib
|
||||
run: cargo fmt --all -- --check
|
||||
working-directory: frontend/rust-lib/
|
||||
|
||||
- name: clippy rust-lib
|
||||
run: cargo clippy --all-targets -- -D warnings
|
||||
working-directory: frontend/rust-lib
|
||||
|
||||
- name: "Debug: show Appflowy-Cloud container logs"
|
||||
if: failure()
|
||||
working-directory: AppFlowy-Cloud
|
||||
run: |
|
||||
docker compose logs appflowy_cloud
|
||||
|
||||
- name: Clean up Docker images
|
||||
run: |
|
||||
docker image prune -af
|
||||
|
||||
@@ -10,7 +10,7 @@ on:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
FLUTTER_VERSION: "3.19.0"
|
||||
FLUTTER_VERSION: "3.22.0"
|
||||
RUST_TOOLCHAIN: "1.77.2"
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
name: Tauri-CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
@@ -11,28 +12,47 @@ env:
|
||||
NODE_VERSION: "18.16.0"
|
||||
PNPM_VERSION: "8.5.0"
|
||||
RUST_TOOLCHAIN: "1.77.2"
|
||||
CARGO_MAKE_VERSION: "0.36.6"
|
||||
CI: true
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
tauri-build:
|
||||
if: github.event.pull_request.draft != true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ ubuntu-20.04 ]
|
||||
tauri-build-self-hosted:
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: self-hosted
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
env:
|
||||
CI: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: install frontend dependencies
|
||||
working-directory: frontend/appflowy_web_app
|
||||
run: |
|
||||
mkdir dist
|
||||
pnpm install
|
||||
cd src-tauri && cargo build
|
||||
|
||||
- name: Maximize build space (ubuntu only)
|
||||
if: matrix.platform == 'ubuntu-20.04'
|
||||
- name: test and lint
|
||||
working-directory: frontend/appflowy_web_app
|
||||
run: |
|
||||
pnpm run lint:tauri
|
||||
|
||||
- uses: tauri-apps/tauri-action@v0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tauriScript: pnpm tauri
|
||||
projectPath: frontend/appflowy_web_app
|
||||
args: "--debug"
|
||||
|
||||
tauri-build-ubuntu:
|
||||
if: github.event.pull_request.head.repo.full_name != github.repository
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Maximize build space
|
||||
run: |
|
||||
sudo rm -rf /usr/share/dotnet
|
||||
sudo rm -rf /opt/ghc
|
||||
@@ -61,36 +81,27 @@ jobs:
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- name: Rust cache
|
||||
uses: swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: "./frontend/appflowy_web_app/src-tauri -> target"
|
||||
|
||||
- name: Node_modules cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: frontend/appflowy_web_app/node_modules
|
||||
key: node-modules-${{ runner.os }}
|
||||
|
||||
- name: install dependencies (windows only)
|
||||
if: matrix.platform == 'windows-latest'
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo install --force duckscript_cli
|
||||
vcpkg integrate install
|
||||
|
||||
- name: install dependencies (ubuntu only)
|
||||
if: matrix.platform == 'ubuntu-20.04'
|
||||
- name: install dependencies
|
||||
working-directory: frontend
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
|
||||
|
||||
- name: install cargo-make
|
||||
- uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-make@${{ env.CARGO_MAKE_VERSION }}
|
||||
|
||||
- name: install tauri deps tools
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo install --force cargo-make
|
||||
cargo make appflowy-tauri-deps-tools
|
||||
shell: bash
|
||||
|
||||
- name: install frontend dependencies
|
||||
working-directory: frontend/appflowy_web_app
|
||||
|
||||
@@ -52,11 +52,11 @@ jobs:
|
||||
working-directory: frontend/appflowy_web_app
|
||||
run: |
|
||||
pnpm install
|
||||
- name: test and lint
|
||||
- name: Run lint check
|
||||
working-directory: frontend/appflowy_web_app
|
||||
run: |
|
||||
pnpm run lint
|
||||
pnpm run test:unit
|
||||
|
||||
- name: build and analyze
|
||||
working-directory: frontend/appflowy_web_app
|
||||
run: |
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
name: WEB-CI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
build:
|
||||
description: 'Build the web app'
|
||||
required: true
|
||||
default: 'true'
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
NODE_VERSION: "18.16.0"
|
||||
PNPM_VERSION: "8.5.0"
|
||||
RUST_TOOLCHAIN: "1.77.2"
|
||||
CARGO_MAKE_VERSION: "0.36.6"
|
||||
|
||||
jobs:
|
||||
web-build:
|
||||
if: github.event.pull_request.draft != true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ ubuntu-latest ]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Cache Rust Dependencies
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
key: rust-dependencies-${{ runner.os }}
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
frontend/appflowy_web/appflowy_wasm
|
||||
|
||||
# TODO: Can combine caching deps and node_modules in one
|
||||
# See Glob patterns: https://github.com/actions/toolkit/tree/main/packages/glob
|
||||
- name: Cache Node.js dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: npm-${{ runner.os }}
|
||||
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: frontend/appflowy_web/node_modules
|
||||
key: node-modules-${{ runner.os }}
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- name: Install wasm-pack
|
||||
run: cargo install wasm-pack
|
||||
|
||||
- uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-make@${{ env.CARGO_MAKE_VERSION }}
|
||||
|
||||
- name: install dependencies
|
||||
if: matrix.platform == 'ubuntu-latest'
|
||||
working-directory: frontend
|
||||
run: |
|
||||
sudo apt-get update
|
||||
npm install -g pnpm@${{ env.PNPM_VERSION }}
|
||||
cargo make install_web_protobuf
|
||||
|
||||
- name: Build
|
||||
working-directory: frontend/appflowy_web
|
||||
run: |
|
||||
pnpm install
|
||||
pnpm run build_release_wasm
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Cypress Tests
|
||||
name: Web Code Coverage
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -6,6 +6,7 @@ on:
|
||||
- ".github/workflows/web2_ci.yaml"
|
||||
- "frontend/appflowy_web_app/**"
|
||||
- "frontend/resources/**"
|
||||
|
||||
env:
|
||||
NODE_VERSION: "18.16.0"
|
||||
PNPM_VERSION: "8.5.0"
|
||||
@@ -13,7 +14,7 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
cypress-run:
|
||||
test:
|
||||
if: github.event.pull_request.draft != true
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
@@ -45,4 +46,20 @@ jobs:
|
||||
component: true
|
||||
build: pnpm run build
|
||||
start: pnpm run start
|
||||
browser: chrome
|
||||
browser: chrome
|
||||
|
||||
- name: Jest run
|
||||
working-directory: frontend/appflowy_web_app
|
||||
run: |
|
||||
pnpm run test:unit
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v2
|
||||
with:
|
||||
token: cf9245e0-e136-4e21-b0ee-35755fa0c493
|
||||
files: frontend/appflowy_web_app/coverage/jest/lcov.info,frontend/appflowy_web_app/coverage/cypress/lcov.info
|
||||
flags: appflowy_web_app
|
||||
name: frontend/appflowy_web_app
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
|
||||
+50
-1
@@ -1,4 +1,53 @@
|
||||
# Release Notes
|
||||
## Version 0.6.5 - 24/07/2024
|
||||
### New Features
|
||||
- Publish a Database to the Web
|
||||
|
||||
## Version 0.6.4 - 16/07/2024
|
||||
### New Features
|
||||
- Enhanced the message style on the AI chat page.
|
||||
- Added the ability to choose cursor color and selection color from a palette in settings page.
|
||||
### Bug Fixes
|
||||
- Optimized the performance for loading recent pages.
|
||||
- Fixed an issue where the cursor would jump randomly when typing in the document title on mobile.
|
||||
|
||||
## Version 0.6.3 - 08/07/2024
|
||||
### New Features
|
||||
- Publish a Document to the Web
|
||||
|
||||
## Version 0.6.2 - 01/07/2024
|
||||
### New Features
|
||||
- Added support for duplicating spaces.
|
||||
- Added support for moving pages across spaces.
|
||||
- Undo markdown formatting with `Ctrl + Z` or `Cmd + Z`.
|
||||
- Improved shortcuts settings UI.
|
||||
### Bug Fixes
|
||||
- Fixed unable to zoom in with `Ctrl` and `+` or `Cmd` and `+` on some keyboards.
|
||||
- Fixed unable to paste nested lists in existing lists.
|
||||
|
||||
## Version 0.6.1 - 22/06/2024
|
||||
### New Features
|
||||
- Introduced the "Space" feature to help you organize your pages more efficiently.
|
||||
### Bug Fixes
|
||||
- Resolved shortcut conflicts on the board page.
|
||||
- Resolved an issue where underscores could cause the editor to freeze.
|
||||
|
||||
## Version 0.6.0 - 19/06/2024
|
||||
### New Features
|
||||
- Introduced the "Space" feature to help you organize your pages more efficiently.
|
||||
### Bug Fixes
|
||||
- Resolved shortcut conflicts on the board page.
|
||||
- Resolved an issue where underscores could cause the editor to freeze.
|
||||
|
||||
## Version 0.5.9 - 06/06/2024
|
||||
### New Features
|
||||
- Revamped the sidebar for both Desktop and Mobile.
|
||||
- Added support for embedding videos in documents.
|
||||
- Introduced a hotkey (Cmd/Ctrl + 0) to reset the app scale.
|
||||
- Supported searching the workspace by page title.
|
||||
### Bug Fixes
|
||||
- Fixed the issue preventing the use of Backspace to delete words in Kanban boards.
|
||||
|
||||
## Version 0.5.8 - 05/20/2024
|
||||
### New Features
|
||||
- Improvement to the Callout block to insert new lines
|
||||
@@ -105,7 +154,7 @@
|
||||
- Fixed a bug where newly created rows were not being automatically sorted.
|
||||
- Fixed issues related to deleting a sorting field or sort not removing existing sorts properly.
|
||||
### Notes
|
||||
- Windows 7, Windows 8, and iOS 11 are not yet supported due to the upgrade to Flutter 3.19.0.
|
||||
- Windows 7, Windows 8, and iOS 11 are not yet supported due to the upgrade to Flutter 3.22.0.
|
||||
|
||||
## Version 0.4.9 - 02/17/2024
|
||||
### Bug Fixes
|
||||
|
||||
Vendored
+1
-2
@@ -13,7 +13,6 @@
|
||||
"type": "dart",
|
||||
"env": {
|
||||
"RUST_LOG": "debug",
|
||||
"RUST_BACKTRACE": "1"
|
||||
},
|
||||
// uncomment the following line to testing performance.
|
||||
// "flutterMode": "profile",
|
||||
@@ -138,4 +137,4 @@
|
||||
"cwd": "${workspaceRoot}/appflowy_tauri/"
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
|
||||
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
|
||||
CARGO_MAKE_CRATE_NAME = "dart-ffi"
|
||||
LIB_NAME = "dart_ffi"
|
||||
APPFLOWY_VERSION = "0.5.8"
|
||||
APPFLOWY_VERSION = "0.6.6"
|
||||
FLUTTER_DESKTOP_FEATURES = "dart"
|
||||
PRODUCT_NAME = "AppFlowy"
|
||||
MACOSX_DEPLOYMENT_TARGET = "11.0"
|
||||
@@ -50,7 +50,6 @@ APP_ENVIRONMENT = "local"
|
||||
FLUTTER_FLOWY_SDK_PATH = "appflowy_flutter/packages/appflowy_backend"
|
||||
TAURI_BACKEND_SERVICE_PATH = "appflowy_tauri/src/services/backend"
|
||||
WEB_BACKEND_SERVICE_PATH = "appflowy_web/src/services/backend"
|
||||
WEB_LIB_PATH = "appflowy_web/wasm-libs/af-wasm"
|
||||
TAURI_APP_BACKEND_SERVICE_PATH = "appflowy_web_app/src/application/services/tauri-services/backend"
|
||||
# Test default config
|
||||
TEST_CRATE_TYPE = "cdylib"
|
||||
|
||||
@@ -52,8 +52,8 @@ android {
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "io.appflowy.appflowy"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 33
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 34
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
multiDexEnabled true
|
||||
|
||||
@@ -58,4 +58,11 @@
|
||||
<action android:name="android.support.customtabs.action.CustomTabsService" />
|
||||
</intent>
|
||||
</queries>
|
||||
<!--
|
||||
Media access permissions.
|
||||
Android 13 or higher.
|
||||
Used for VideoBlock (edia_kit)
|
||||
-->
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
||||
</manifest>
|
||||
@@ -42,7 +42,7 @@ void main() {
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
// reanme the name of the anon user
|
||||
// rename the name of the anon user
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.account);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
@@ -47,11 +47,11 @@ void main() {
|
||||
);
|
||||
await tester.tapButton(find.byType(SignInOutButton));
|
||||
|
||||
tester.expectToSeeText(LocaleKeys.button_confirm.tr());
|
||||
await tester.tapButtonWithName(LocaleKeys.button_confirm.tr());
|
||||
tester.expectToSeeText(LocaleKeys.button_ok.tr());
|
||||
await tester.tapButtonWithName(LocaleKeys.button_ok.tr());
|
||||
|
||||
// Go to the sign in page again
|
||||
await tester.pumpAndSettle(const Duration(seconds: 1));
|
||||
await tester.pumpAndSettle(const Duration(seconds: 5));
|
||||
tester.expectToSeeGoogleLoginButton();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'anon_user_continue_test.dart' as anon_user_continue_test;
|
||||
import 'appflowy_cloud_auth_test.dart' as appflowy_cloud_auth_test;
|
||||
import 'empty_test.dart' as preset_af_cloud_env_test;
|
||||
// import 'document_sync_test.dart' as document_sync_test;
|
||||
import 'user_setting_sync_test.dart' as user_sync_test;
|
||||
import 'workspace/change_name_and_icon_test.dart'
|
||||
as change_workspace_name_and_icon_test;
|
||||
@@ -10,13 +9,8 @@ import 'workspace/collaborative_workspace_test.dart'
|
||||
|
||||
Future<void> main() async {
|
||||
preset_af_cloud_env_test.main();
|
||||
|
||||
appflowy_cloud_auth_test.main();
|
||||
|
||||
// document_sync_test.main();
|
||||
|
||||
user_sync_test.main();
|
||||
|
||||
anon_user_continue_test.main();
|
||||
|
||||
// workspace
|
||||
|
||||
@@ -46,7 +46,6 @@ void main() {
|
||||
await tester.ime.insertText(inputContent);
|
||||
expect(find.text(inputContent, findRichText: true), findsOneWidget);
|
||||
|
||||
// TODO(nathan): remove the await
|
||||
// 6 seconds for data sync
|
||||
await tester.waitForSeconds(6);
|
||||
|
||||
|
||||
@@ -50,26 +50,28 @@ void main() {
|
||||
await tester.tapEscButton();
|
||||
|
||||
// wait 2 seconds for the sync to finish
|
||||
await tester.pumpAndSettle(const Duration(seconds: 2));
|
||||
await tester.pumpAndSettle(const Duration(seconds: 6));
|
||||
});
|
||||
|
||||
|
||||
testWidgets('get user icon and name from server', (tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
email: email,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.account);
|
||||
|
||||
// Verify name
|
||||
final profileSetting =
|
||||
tester.widget(find.byType(UserProfileSetting)) as UserProfileSetting;
|
||||
|
||||
expect(profileSetting.name, name);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('get user icon and name from server', (tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
email: email,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.account);
|
||||
|
||||
// Verify name
|
||||
final profileSetting =
|
||||
tester.widget(find.byType(UserProfileSetting)) as UserProfileSetting;
|
||||
|
||||
expect(profileSetting.name, name);
|
||||
});
|
||||
}
|
||||
+51
-52
@@ -2,8 +2,6 @@
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/widgets/loading.dart';
|
||||
@@ -12,14 +10,15 @@ import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/application/auth/af_cloud_mock_auth_service.dart';
|
||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_workspace.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_actions.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/sidebar_workspace.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/user_avatar.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/uuid.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
@@ -33,69 +32,69 @@ import '../../shared/util.dart';
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
final email = '${uuid()}@appflowy.io';
|
||||
// final email = '${uuid()}@appflowy.io';
|
||||
|
||||
group('collaborative workspace', () {
|
||||
// combine the create and delete workspace test to reduce the time
|
||||
testWidgets('create a new workspace, open it and then delete it',
|
||||
(tester) async {
|
||||
// only run the test when the feature flag is on
|
||||
if (!FeatureFlag.collaborativeWorkspace.isOn) {
|
||||
return;
|
||||
}
|
||||
// if (!FeatureFlag.collaborativeWorkspace.isOn) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
email: email,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
// await tester.initializeAppFlowy(
|
||||
// cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
// email: email,
|
||||
// );
|
||||
// await tester.tapGoogleLoginInButton();
|
||||
// await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
const name = 'AppFlowy.IO';
|
||||
// the workspace will be opened after created
|
||||
await tester.createCollaborativeWorkspace(name);
|
||||
// const name = 'AppFlowy.IO';
|
||||
// // the workspace will be opened after created
|
||||
// await tester.createCollaborativeWorkspace(name);
|
||||
|
||||
final loading = find.byType(Loading);
|
||||
await tester.pumpUntilNotFound(loading);
|
||||
// final loading = find.byType(Loading);
|
||||
// await tester.pumpUntilNotFound(loading);
|
||||
|
||||
Finder success;
|
||||
// Finder success;
|
||||
|
||||
final Finder items = find.byType(WorkspaceMenuItem);
|
||||
// final Finder items = find.byType(WorkspaceMenuItem);
|
||||
|
||||
// delete the newly created workspace
|
||||
await tester.openCollaborativeWorkspaceMenu();
|
||||
await tester.pumpUntilFound(items);
|
||||
// // delete the newly created workspace
|
||||
// await tester.openCollaborativeWorkspaceMenu();
|
||||
// await tester.pumpUntilFound(items);
|
||||
|
||||
expect(items, findsNWidgets(2));
|
||||
expect(
|
||||
tester.widget<WorkspaceMenuItem>(items.last).workspace.name,
|
||||
name,
|
||||
);
|
||||
// expect(items, findsNWidgets(2));
|
||||
// expect(
|
||||
// tester.widget<WorkspaceMenuItem>(items.last).workspace.name,
|
||||
// name,
|
||||
// );
|
||||
|
||||
final secondWorkspace = find.byType(WorkspaceMenuItem).last;
|
||||
await tester.hoverOnWidget(
|
||||
secondWorkspace,
|
||||
onHover: () async {
|
||||
// click the more button
|
||||
final moreButton = find.byType(WorkspaceMoreActionList);
|
||||
expect(moreButton, findsOneWidget);
|
||||
await tester.tapButton(moreButton);
|
||||
// click the delete button
|
||||
final deleteButton = find.text(LocaleKeys.button_delete.tr());
|
||||
expect(deleteButton, findsOneWidget);
|
||||
await tester.tapButton(deleteButton);
|
||||
// see the delete confirm dialog
|
||||
final confirm =
|
||||
find.text(LocaleKeys.workspace_deleteWorkspaceHintText.tr());
|
||||
expect(confirm, findsOneWidget);
|
||||
await tester.tapButton(find.text(LocaleKeys.button_ok.tr()));
|
||||
// delete success
|
||||
success = find.text(LocaleKeys.workspace_createSuccess.tr());
|
||||
await tester.pumpUntilFound(success);
|
||||
expect(success, findsOneWidget);
|
||||
await tester.pumpUntilNotFound(success);
|
||||
},
|
||||
);
|
||||
// final secondWorkspace = find.byType(WorkspaceMenuItem).last;
|
||||
// await tester.hoverOnWidget(
|
||||
// secondWorkspace,
|
||||
// onHover: () async {
|
||||
// // click the more button
|
||||
// final moreButton = find.byType(WorkspaceMoreActionList);
|
||||
// expect(moreButton, findsOneWidget);
|
||||
// await tester.tapButton(moreButton);
|
||||
// // click the delete button
|
||||
// final deleteButton = find.text(LocaleKeys.button_delete.tr());
|
||||
// expect(deleteButton, findsOneWidget);
|
||||
// await tester.tapButton(deleteButton);
|
||||
// // see the delete confirm dialog
|
||||
// final confirm =
|
||||
// find.text(LocaleKeys.workspace_deleteWorkspaceHintText.tr());
|
||||
// expect(confirm, findsOneWidget);
|
||||
// await tester.tapButton(find.text(LocaleKeys.button_ok.tr()));
|
||||
// // delete success
|
||||
// success = find.text(LocaleKeys.workspace_createSuccess.tr());
|
||||
// await tester.pumpUntilFound(success);
|
||||
// expect(success, findsOneWidget);
|
||||
// await tester.pumpUntilNotFound(success);
|
||||
// },
|
||||
// );
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -29,6 +29,7 @@ void main() {
|
||||
},
|
||||
);
|
||||
await tester.tapButtonWithName(LocaleKeys.button_delete.tr());
|
||||
await tester.tapOKButton();
|
||||
expect(find.text(name), findsNothing);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'board_row_test.dart' as board_row_test;
|
||||
import 'board_add_row_test.dart' as board_add_row_test;
|
||||
import 'board_group_test.dart' as board_group_test;
|
||||
import 'board_row_test.dart' as board_row_test;
|
||||
|
||||
void startTesting() {
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Board integration tests
|
||||
|
||||
@@ -41,15 +41,16 @@ void main() {
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
await tester.hoverOnFirstRowOfGrid();
|
||||
await tester.hoverOnFirstRowOfGrid(() async {
|
||||
// Open the row menu and then click the delete
|
||||
await tester.tapRowMenuButtonInGrid();
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tapDeleteOnRowMenu();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Open the row menu and then click the delete
|
||||
await tester.tapRowMenuButtonInGrid();
|
||||
await tester.tapDeleteOnRowMenu();
|
||||
|
||||
// 3 initial rows - 1 deleted
|
||||
await tester.assertNumberOfRowsInGridPage(2);
|
||||
await tester.pumpAndSettle();
|
||||
// 3 initial rows - 1 deleted
|
||||
await tester.assertNumberOfRowsInGridPage(2);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('check number of row indicator in the initial grid',
|
||||
|
||||
+3
@@ -66,6 +66,7 @@ void main() {
|
||||
LogicalKeyboardKey.keyR,
|
||||
],
|
||||
tester: tester,
|
||||
withKeyUp: true,
|
||||
);
|
||||
expect(first.attributes[blockComponentAlign], rightAlignmentKey);
|
||||
|
||||
@@ -77,6 +78,7 @@ void main() {
|
||||
LogicalKeyboardKey.keyE,
|
||||
],
|
||||
tester: tester,
|
||||
withKeyUp: true,
|
||||
);
|
||||
expect(first.attributes[blockComponentAlign], centerAlignmentKey);
|
||||
|
||||
@@ -88,6 +90,7 @@ void main() {
|
||||
LogicalKeyboardKey.keyL,
|
||||
],
|
||||
tester: tester,
|
||||
withKeyUp: true,
|
||||
);
|
||||
expect(first.attributes[blockComponentAlign], leftAlignmentKey);
|
||||
});
|
||||
|
||||
+68
-27
@@ -1,10 +1,11 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
@@ -116,6 +117,53 @@ void main() {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('paste bulleted list in numbered list', (tester) async {
|
||||
const inAppJson =
|
||||
'{"document":{"type":"page","children":[{"type":"bulleted_list","children":[{"type":"bulleted_list","data":{"delta":[{"insert":"World"}]}}],"data":{"delta":[{"insert":"Hello"}]}}]}}';
|
||||
|
||||
await tester.pasteContent(
|
||||
inAppJson: inAppJson,
|
||||
beforeTest: (editorState) async {
|
||||
final transaction = editorState.transaction;
|
||||
// Insert two numbered list nodes
|
||||
// 1. Parent One
|
||||
// 2.
|
||||
transaction.insertNodes(
|
||||
[0],
|
||||
[
|
||||
Node(
|
||||
type: NumberedListBlockKeys.type,
|
||||
attributes: {
|
||||
'delta': [
|
||||
{"insert": "One"},
|
||||
],
|
||||
},
|
||||
),
|
||||
Node(
|
||||
type: NumberedListBlockKeys.type,
|
||||
attributes: {'delta': []},
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
// Set the selection to the second numbered list node (which has empty delta)
|
||||
transaction.afterSelection = Selection.collapsed(Position(path: [1]));
|
||||
|
||||
await editorState.apply(transaction);
|
||||
await tester.pumpAndSettle();
|
||||
},
|
||||
(editorState) {
|
||||
final secondNode = editorState.getNodeAtPath([1]);
|
||||
expect(secondNode?.delta?.toPlainText(), 'Hello');
|
||||
expect(secondNode?.children.length, 1);
|
||||
|
||||
final childNode = secondNode?.children.first;
|
||||
expect(childNode?.delta?.toPlainText(), 'World');
|
||||
expect(childNode?.type, BulletedListBlockKeys.type);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('paste image(png) from memory', (tester) async {
|
||||
@@ -210,42 +258,33 @@ void main() {
|
||||
testWidgets('paste the html content contains section', (tester) async {
|
||||
const html =
|
||||
'''<meta charset='utf-8'><section style="margin: 0px 8px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgba(255, 255, 255, 0.6); font-family: system-ui, -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(25, 25, 25); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><span style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgb(0, 160, 113);"><strong style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important;">AppFlowy</strong></span></section><section style="margin: 0px 8px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgba(255, 255, 255, 0.6); font-family: system-ui, -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(25, 25, 25); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><span style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgb(0, 160, 113);"><strong style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important;">Hello World</strong></span></section>''';
|
||||
await tester.pasteContent(
|
||||
html: html,
|
||||
(editorState) {
|
||||
expect(editorState.document.root.children.length, 2);
|
||||
final node1 = editorState.getNodeAtPath([0])!;
|
||||
final node2 = editorState.getNodeAtPath([1])!;
|
||||
expect(node1.type, ParagraphBlockKeys.type);
|
||||
expect(node2.type, ParagraphBlockKeys.type);
|
||||
},
|
||||
);
|
||||
await tester.pasteContent(html: html, (editorState) {
|
||||
expect(editorState.document.root.children.length, 2);
|
||||
final node1 = editorState.getNodeAtPath([0])!;
|
||||
final node2 = editorState.getNodeAtPath([1])!;
|
||||
expect(node1.type, ParagraphBlockKeys.type);
|
||||
expect(node2.type, ParagraphBlockKeys.type);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('paste the html from google translation', (tester) async {
|
||||
const html =
|
||||
'''<meta charset='utf-8'><section style="margin: 0px 8px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgba(255, 255, 255, 0.6); font-family: system-ui, -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(25, 25, 25); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><span style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgb(0, 160, 113);"><strong style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important;"><font style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; vertical-align: inherit;"><font style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; vertical-align: inherit;">new force</font></font></strong></span></section><section style="margin: 0px 8px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgba(255, 255, 255, 0.6); font-family: system-ui, -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(25, 25, 25); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><span style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgb(0, 160, 113);"><strong style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important;"><font style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; vertical-align: inherit;"><font style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; vertical-align: inherit;">Assessment focus: potential motivations, empathy</font></font></strong></span></section><section style="margin: 0px 8px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgba(255, 255, 255, 0.6); font-family: system-ui, -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(25, 25, 25); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><br style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"></section><section style="margin: 0px 8px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgba(255, 255, 255, 0.6); font-family: system-ui, -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(25, 25, 25); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><font style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-Line truncated
|
||||
await tester.pasteContent(
|
||||
html: html,
|
||||
(editorState) {
|
||||
expect(editorState.document.root.children.length, 8);
|
||||
},
|
||||
);
|
||||
await tester.pasteContent(html: html, (editorState) {
|
||||
expect(editorState.document.root.children.length, 8);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'auto convert url to link preview block',
|
||||
(widgetTester) async {
|
||||
(tester) async {
|
||||
const url = 'https://appflowy.io';
|
||||
await widgetTester.pasteContent(
|
||||
plainText: url,
|
||||
(editorState) {
|
||||
expect(editorState.document.root.children.length, 2);
|
||||
final node = editorState.getNodeAtPath([0])!;
|
||||
expect(node.type, LinkPreviewBlockKeys.type);
|
||||
expect(node.attributes[LinkPreviewBlockKeys.url], url);
|
||||
},
|
||||
);
|
||||
await tester.pasteContent(plainText: url, (editorState) {
|
||||
expect(editorState.document.root.children.length, 2);
|
||||
final node = editorState.getNodeAtPath([0])!;
|
||||
expect(node.type, LinkPreviewBlockKeys.type);
|
||||
expect(node.attributes[LinkPreviewBlockKeys.url], url);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -256,6 +295,7 @@ extension on WidgetTester {
|
||||
Future<void> Function(EditorState editorState)? beforeTest,
|
||||
String? plainText,
|
||||
String? html,
|
||||
String? inAppJson,
|
||||
(String, Uint8List?)? image,
|
||||
}) async {
|
||||
await initializeAppFlowy();
|
||||
@@ -271,6 +311,7 @@ extension on WidgetTester {
|
||||
ClipboardServiceData(
|
||||
plainText: plainText,
|
||||
html: html,
|
||||
inAppJson: inAppJson,
|
||||
image: image,
|
||||
),
|
||||
);
|
||||
|
||||
+2
@@ -111,6 +111,7 @@ Future<void> triggerReferenceDocumentBySlashMenu(WidgetTester tester) async {
|
||||
LogicalKeyboardKey.enter,
|
||||
],
|
||||
tester: tester,
|
||||
withKeyUp: true,
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
@@ -129,6 +130,7 @@ Future<void> enterDocumentText(WidgetTester tester) async {
|
||||
LogicalKeyboardKey.keyT,
|
||||
],
|
||||
tester: tester,
|
||||
withKeyUp: true,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
+2
@@ -19,12 +19,14 @@ void main() {
|
||||
// Duplicate
|
||||
await tester.openMoreViewActions();
|
||||
await tester.duplicateByMoreViewActions();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(pageFinder, findsNWidgets(2));
|
||||
|
||||
// Delete
|
||||
await tester.openMoreViewActions();
|
||||
await tester.deleteByMoreViewActions();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(pageFinder, findsNWidgets(1));
|
||||
});
|
||||
|
||||
@@ -16,6 +16,8 @@ import 'document_with_image_block_test.dart' as document_with_image_block_test;
|
||||
import 'document_with_inline_math_equation_test.dart'
|
||||
as document_with_inline_math_equation_test;
|
||||
import 'document_with_inline_page_test.dart' as document_with_inline_page_test;
|
||||
import 'document_with_multi_image_block_test.dart'
|
||||
as document_with_multi_image_block_test;
|
||||
import 'document_with_outline_block_test.dart' as document_with_outline_block;
|
||||
import 'document_with_toggle_list_test.dart' as document_with_toggle_list_test;
|
||||
import 'edit_document_test.dart' as document_edit_test;
|
||||
@@ -38,6 +40,7 @@ void startTesting() {
|
||||
document_text_direction_test.main();
|
||||
document_option_action_test.main();
|
||||
document_with_image_block_test.main();
|
||||
document_with_multi_image_block_test.main();
|
||||
document_inline_page_reference_test.main();
|
||||
document_more_actions_test.main();
|
||||
}
|
||||
+6
-6
@@ -130,24 +130,24 @@ void main() {
|
||||
final searchEmojiTextField = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is TextField &&
|
||||
widget.decoration!.hintText == LocaleKeys.emoji_search.tr(),
|
||||
widget.decoration!.hintText == LocaleKeys.search_label.tr(),
|
||||
);
|
||||
await tester.enterText(
|
||||
searchEmojiTextField,
|
||||
'hand',
|
||||
'punch',
|
||||
);
|
||||
|
||||
// change skin tone
|
||||
await tester.editor.changeEmojiSkinTone(EmojiSkinTone.dark);
|
||||
|
||||
// select an icon with skin tone
|
||||
const hand = '👋🏿';
|
||||
await tester.tapEmoji(hand);
|
||||
tester.expectToSeeDocumentIcon(hand);
|
||||
const punch = '👊🏿';
|
||||
await tester.tapEmoji(punch);
|
||||
tester.expectToSeeDocumentIcon(punch);
|
||||
tester.expectViewHasIcon(
|
||||
gettingStarted,
|
||||
ViewLayoutPB.Document,
|
||||
hand,
|
||||
punch,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
+71
-8
@@ -1,21 +1,22 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy/core/config/kv.dart';
|
||||
import 'package:appflowy/core/config/kv_keys.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/custom_image_block_component.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/embed_image_url_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/custom_image_block_component/custom_image_block_component.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/image_placeholder.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/resizeable_image.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/unsplash_image_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu/upload_image_menu.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu/widgets/embed_image_url_widget.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart'
|
||||
hide UploadImageMenu, ResizableImage;
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.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 'package:path/path.dart' as p;
|
||||
@@ -36,7 +37,7 @@ void main() {
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: LocaleKeys.document_plugins_image_addAnImage.tr(),
|
||||
name: LocaleKeys.document_plugins_image_addAnImageDesktop.tr(),
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
@@ -84,7 +85,7 @@ void main() {
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: LocaleKeys.document_plugins_image_addAnImage.tr(),
|
||||
name: LocaleKeys.document_plugins_image_addAnImageDesktop.tr(),
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
@@ -137,7 +138,7 @@ void main() {
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: LocaleKeys.document_plugins_image_addAnImage.tr(),
|
||||
name: LocaleKeys.document_plugins_image_addAnImageDesktop.tr(),
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
@@ -161,5 +162,67 @@ void main() {
|
||||
expect(find.byType(UnsplashImageWidget), findsOneWidget);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('insert two images from local file at once', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: LocaleKeys.document_plugins_image_addAnImageDesktop.tr(),
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.editor.showSlashMenu();
|
||||
await tester.editor.tapSlashMenuItemWithName('Image');
|
||||
expect(find.byType(CustomImageBlockComponent), findsOneWidget);
|
||||
expect(find.byType(ImagePlaceholder), findsOneWidget);
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(ImagePlaceholder),
|
||||
matching: find.byType(AppFlowyPopover),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.byType(UploadImageMenu), findsOneWidget);
|
||||
|
||||
final firstImage =
|
||||
await rootBundle.load('assets/test/images/sample.jpeg');
|
||||
final secondImage =
|
||||
await rootBundle.load('assets/test/images/sample.gif');
|
||||
final tempDirectory = await getTemporaryDirectory();
|
||||
|
||||
final firstImagePath = p.join(tempDirectory.path, 'sample.jpeg');
|
||||
final firstFile = File(firstImagePath)
|
||||
..writeAsBytesSync(firstImage.buffer.asUint8List());
|
||||
|
||||
final secondImagePath = p.join(tempDirectory.path, 'sample.gif');
|
||||
final secondFile = File(secondImagePath)
|
||||
..writeAsBytesSync(secondImage.buffer.asUint8List());
|
||||
|
||||
mockPickFilePaths(paths: [firstImagePath, secondImagePath]);
|
||||
|
||||
await getIt<KeyValueStorage>().set(KVKeys.kCloudType, '0');
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.document_imageBlock_upload_placeholder.tr(),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(ResizableImage), findsNWidgets(2));
|
||||
|
||||
final firstNode =
|
||||
tester.editor.getCurrentEditorState().getNodeAtPath([0])!;
|
||||
expect(firstNode.type, ImageBlockKeys.type);
|
||||
expect(firstNode.attributes[ImageBlockKeys.url], isNotEmpty);
|
||||
|
||||
final secondNode =
|
||||
tester.editor.getCurrentEditorState().getNodeAtPath([0])!;
|
||||
expect(secondNode.type, ImageBlockKeys.type);
|
||||
expect(secondNode.attributes[ImageBlockKeys.url], isNotEmpty);
|
||||
|
||||
// remove the temp files
|
||||
await Future.wait([firstFile.delete(), secondFile.delete()]);
|
||||
});
|
||||
});
|
||||
}
|
||||
+287
@@ -0,0 +1,287 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy/core/config/kv.dart';
|
||||
import 'package:appflowy/core/config/kv_keys.dart';
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/multi_image_block_component/multi_image_block_component.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/multi_image_block_component/multi_image_placeholder.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/multi_image_layouts.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu/upload_image_menu.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu/widgets/embed_image_url_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||
import 'package:appflowy/shared/appflowy_network_image.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/image_viewer/interactive_image_toolbar.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/image_viewer/interactive_image_viewer.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
import '../../shared/mock/mock_file_picker.dart';
|
||||
import '../../shared/util.dart';
|
||||
import '../board/board_hide_groups_test.dart';
|
||||
|
||||
void main() {
|
||||
setUp(() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
});
|
||||
|
||||
group('multi image block in document', () {
|
||||
testWidgets('insert images from local and use interactive viewer',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'multi image block test',
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.editor.showSlashMenu();
|
||||
await tester.editor.tapSlashMenuItemWithName('Photo gallery');
|
||||
expect(find.byType(MultiImageBlockComponent), findsOneWidget);
|
||||
expect(find.byType(MultiImagePlaceholder), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byType(MultiImagePlaceholder));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(UploadImageMenu), findsOneWidget);
|
||||
|
||||
final firstImage =
|
||||
await rootBundle.load('assets/test/images/sample.jpeg');
|
||||
final secondImage =
|
||||
await rootBundle.load('assets/test/images/sample.gif');
|
||||
final tempDirectory = await getTemporaryDirectory();
|
||||
|
||||
final firstImagePath = p.join(tempDirectory.path, 'sample.jpeg');
|
||||
final firstFile = File(firstImagePath)
|
||||
..writeAsBytesSync(firstImage.buffer.asUint8List());
|
||||
|
||||
final secondImagePath = p.join(tempDirectory.path, 'sample.gif');
|
||||
final secondFile = File(secondImagePath)
|
||||
..writeAsBytesSync(secondImage.buffer.asUint8List());
|
||||
|
||||
mockPickFilePaths(paths: [firstImagePath, secondImagePath]);
|
||||
|
||||
await getIt<KeyValueStorage>().set(KVKeys.kCloudType, '0');
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.document_imageBlock_upload_placeholder.tr(),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(ImageBrowserLayout), findsOneWidget);
|
||||
final node = tester.editor.getCurrentEditorState().getNodeAtPath([0])!;
|
||||
expect(node.type, MultiImageBlockKeys.type);
|
||||
|
||||
final data = MultiImageData.fromJson(
|
||||
node.attributes[MultiImageBlockKeys.images],
|
||||
);
|
||||
|
||||
expect(data.images.length, 2);
|
||||
|
||||
// Start using the interactive viewer to view the image(s)
|
||||
final imageFinder = find
|
||||
.byWidgetPredicate(
|
||||
(w) =>
|
||||
w is Image &&
|
||||
w.image is FileImage &&
|
||||
(w.image as FileImage).file.path.endsWith('.jpeg'),
|
||||
)
|
||||
.first;
|
||||
await tester.tap(imageFinder);
|
||||
await tester.pump(kDoubleTapMinTime);
|
||||
await tester.tap(imageFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final ivFinder = find.byType(InteractiveImageViewer);
|
||||
expect(ivFinder, findsOneWidget);
|
||||
|
||||
// go to next image
|
||||
await tester.tap(find.byFlowySvg(FlowySvgs.arrow_right_s));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Expect image to end with .gif
|
||||
final gifImageFinder = find.byWidgetPredicate(
|
||||
(w) =>
|
||||
w is Image &&
|
||||
w.image is FileImage &&
|
||||
(w.image as FileImage).file.path.endsWith('.gif'),
|
||||
);
|
||||
|
||||
gifImageFinder.evaluate();
|
||||
expect(gifImageFinder.found.length, 2);
|
||||
|
||||
// go to previous image
|
||||
await tester.tap(find.byFlowySvg(FlowySvgs.arrow_left_s));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
gifImageFinder.evaluate();
|
||||
expect(gifImageFinder.found.length, 1);
|
||||
|
||||
// remove the temp files
|
||||
await Future.wait([firstFile.delete(), secondFile.delete()]);
|
||||
});
|
||||
|
||||
testWidgets('insert and delete images from network', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'multi image block test',
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.editor.showSlashMenu();
|
||||
await tester.editor.tapSlashMenuItemWithName('Photo gallery');
|
||||
expect(find.byType(MultiImageBlockComponent), findsOneWidget);
|
||||
expect(find.byType(MultiImagePlaceholder), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byType(MultiImagePlaceholder));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(UploadImageMenu), findsOneWidget);
|
||||
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.document_imageBlock_embedLink_label.tr(),
|
||||
);
|
||||
|
||||
const url =
|
||||
'https://images.unsplash.com/photo-1469474968028-56623f02e42e?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&dl=david-marcu-78A265wPiO4-unsplash.jpg&w=640';
|
||||
await tester.enterText(
|
||||
find.descendant(
|
||||
of: find.byType(EmbedImageUrlWidget),
|
||||
matching: find.byType(TextField),
|
||||
),
|
||||
url,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tapButton(
|
||||
find.descendant(
|
||||
of: find.byType(EmbedImageUrlWidget),
|
||||
matching: find.text(
|
||||
LocaleKeys.document_imageBlock_embedLink_label.tr(),
|
||||
findRichText: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(ImageBrowserLayout), findsOneWidget);
|
||||
final node = tester.editor.getCurrentEditorState().getNodeAtPath([0])!;
|
||||
expect(node.type, MultiImageBlockKeys.type);
|
||||
|
||||
final data = MultiImageData.fromJson(
|
||||
node.attributes[MultiImageBlockKeys.images],
|
||||
);
|
||||
|
||||
expect(data.images.length, 1);
|
||||
|
||||
final imageFinder = find
|
||||
.byWidgetPredicate(
|
||||
(w) => w is FlowyNetworkImage && w.url == url,
|
||||
)
|
||||
.first;
|
||||
|
||||
// Insert two images from network
|
||||
for (int i = 0; i < 2; i++) {
|
||||
// Hover on the image to show the image toolbar
|
||||
await tester.hoverOnWidget(
|
||||
imageFinder,
|
||||
onHover: () async {
|
||||
// Click on the add
|
||||
final addFinder = find.descendant(
|
||||
of: find.byType(MultiImageMenu),
|
||||
matching: find.byFlowySvg(FlowySvgs.add_s),
|
||||
);
|
||||
|
||||
expect(addFinder, findsOneWidget);
|
||||
await tester.tap(addFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.document_imageBlock_embedLink_label.tr(),
|
||||
);
|
||||
|
||||
await tester.enterText(
|
||||
find.descendant(
|
||||
of: find.byType(EmbedImageUrlWidget),
|
||||
matching: find.byType(TextField),
|
||||
),
|
||||
url,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tapButton(
|
||||
find.descendant(
|
||||
of: find.byType(EmbedImageUrlWidget),
|
||||
matching: find.text(
|
||||
LocaleKeys.document_imageBlock_embedLink_label.tr(),
|
||||
findRichText: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// There should be 4 images visible now, where 2 are thumbnails
|
||||
expect(find.byType(ThumbnailItem), findsNWidgets(3));
|
||||
|
||||
// And all three use ImageRender
|
||||
expect(find.byType(ImageRender), findsNWidgets(4));
|
||||
|
||||
// Hover on and delete the first thumbnail image
|
||||
await tester.hoverOnWidget(find.byType(ThumbnailItem).first);
|
||||
|
||||
final deleteFinder = find
|
||||
.descendant(
|
||||
of: find.byType(ThumbnailItem),
|
||||
matching: find.byFlowySvg(FlowySvgs.delete_s),
|
||||
)
|
||||
.first;
|
||||
|
||||
expect(deleteFinder, findsOneWidget);
|
||||
await tester.tap(deleteFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(ImageRender), findsNWidgets(3));
|
||||
|
||||
// Delete one from interactive viewer
|
||||
await tester.tap(imageFinder);
|
||||
await tester.pump(kDoubleTapMinTime);
|
||||
await tester.tap(imageFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final ivFinder = find.byType(InteractiveImageViewer);
|
||||
expect(ivFinder, findsOneWidget);
|
||||
|
||||
await tester.tap(
|
||||
find.descendant(
|
||||
of: find.byType(InteractiveImageToolbar),
|
||||
matching: find.byFlowySvg(FlowySvgs.delete_s),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(InteractiveImageViewer), findsNothing);
|
||||
|
||||
// There should be 1 image and the thumbnail for said image still visible
|
||||
expect(find.byType(ImageRender), findsNWidgets(2));
|
||||
});
|
||||
});
|
||||
}
|
||||
+12
-12
@@ -1,5 +1,5 @@
|
||||
import 'package:appflowy/workspace/application/settings/settings_dialog_bloc.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
@@ -8,8 +8,8 @@ import '../../shared/util.dart';
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('board add row test', () {
|
||||
testWidgets('Add card from header', (tester) async {
|
||||
group('notification test', () {
|
||||
testWidgets('enable notification', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
@@ -17,25 +17,25 @@ void main() {
|
||||
await tester.openSettingsPage(SettingsPage.notifications);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final switchFinder = find.byType(Switch);
|
||||
final toggleFinder = find.byType(Toggle).first;
|
||||
|
||||
// Defaults to enabled
|
||||
Switch switchWidget = tester.widget(switchFinder);
|
||||
expect(switchWidget.value, true);
|
||||
Toggle toggleWidget = tester.widget(toggleFinder);
|
||||
expect(toggleWidget.value, true);
|
||||
|
||||
// Disable
|
||||
await tester.tap(switchFinder);
|
||||
await tester.tap(toggleFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
switchWidget = tester.widget(switchFinder);
|
||||
expect(switchWidget.value, false);
|
||||
toggleWidget = tester.widget(toggleFinder);
|
||||
expect(toggleWidget.value, false);
|
||||
|
||||
// Enable again
|
||||
await tester.tap(switchFinder);
|
||||
await tester.tap(toggleFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
switchWidget = tester.widget(switchFinder);
|
||||
expect(switchWidget.value, true);
|
||||
toggleWidget = tester.widget(toggleFinder);
|
||||
expect(toggleWidget.value, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../../shared/auth_operation.dart';
|
||||
import '../../shared/base.dart';
|
||||
import '../../shared/expectation.dart';
|
||||
import '../../shared/settings.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('Settings Billing', () {
|
||||
testWidgets('Local auth cannot see plan+billing', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapSignInAsGuest();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
await tester.openSettings();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// We check that another settings page is present to ensure
|
||||
// it's not a fluke
|
||||
expect(
|
||||
find.text(
|
||||
LocaleKeys.settings_workspacePage_menuLabel.tr(),
|
||||
skipOffstage: false,
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
expect(
|
||||
find.text(
|
||||
LocaleKeys.settings_planPage_menuLabel.tr(),
|
||||
skipOffstage: false,
|
||||
),
|
||||
findsNothing,
|
||||
);
|
||||
|
||||
expect(
|
||||
find.text(
|
||||
LocaleKeys.settings_billingPage_menuLabel.tr(),
|
||||
skipOffstage: false,
|
||||
),
|
||||
findsNothing,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'notifications_settings_test.dart' as notifications_settings_test;
|
||||
import 'settings_billing_test.dart' as settings_billing_test;
|
||||
import 'shortcuts_settings_test.dart' as shortcuts_settings_test;
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
notifications_settings_test.main();
|
||||
settings_billing_test.main();
|
||||
shortcuts_settings_test.main();
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/application/settings/settings_dialog_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/pages/settings_shortcuts_view.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../../shared/keyboard.dart';
|
||||
import '../../shared/util.dart';
|
||||
import '../board/board_hide_groups_test.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('shortcuts test', () {
|
||||
testWidgets('can change and overwrite shortcut', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.shortcuts);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final backspaceCmd =
|
||||
LocaleKeys.settings_shortcutsPage_keybindings_backspace.tr();
|
||||
|
||||
// Input "Delete" into the search field
|
||||
await tester.enterText(find.byType(TextField), backspaceCmd);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.hoverOnWidget(
|
||||
find.descendant(
|
||||
of: find.byType(ShortcutSettingTile),
|
||||
matching: find.text(backspaceCmd),
|
||||
),
|
||||
onHover: () async {
|
||||
await tester.tap(find.byFlowySvg(FlowySvgs.edit_s));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await FlowyTestKeyboard.simulateKeyDownEvent(
|
||||
[
|
||||
LogicalKeyboardKey.delete,
|
||||
LogicalKeyboardKey.enter,
|
||||
],
|
||||
tester: tester,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
},
|
||||
);
|
||||
|
||||
// We expect to see conflict dialog
|
||||
expect(
|
||||
find.text(
|
||||
LocaleKeys.settings_shortcutsPage_conflictDialog_confirmLabel.tr(),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
// Press on confirm label
|
||||
await tester.tap(
|
||||
find.text(
|
||||
LocaleKeys.settings_shortcutsPage_conflictDialog_confirmLabel.tr(),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// We expect the first ShortcutSettingTile to have one
|
||||
// [KeyBadge] with `delete` label
|
||||
final first = tester.widget(find.byType(ShortcutSettingTile).first)
|
||||
as ShortcutSettingTile;
|
||||
expect(
|
||||
first.command.command,
|
||||
'delete',
|
||||
);
|
||||
|
||||
// And the second one which is `Delete left character` to have none
|
||||
// as it will have been overwritten
|
||||
final second = tester.widget(find.byType(ShortcutSettingTile).at(1))
|
||||
as ShortcutSettingTile;
|
||||
expect(
|
||||
second.command.command,
|
||||
'',
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
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/sidebar_folder.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/shared/sidebar_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';
|
||||
@@ -12,8 +12,8 @@ void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('sidebar expand test', () {
|
||||
bool isExpanded({required FolderCategoryType type}) {
|
||||
if (type == FolderCategoryType.private) {
|
||||
bool isExpanded({required FolderSpaceType type}) {
|
||||
if (type == FolderSpaceType.private) {
|
||||
return find
|
||||
.descendant(
|
||||
of: find.byType(PrivateSectionFolder),
|
||||
@@ -30,19 +30,19 @@ void main() {
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
// first time is expanded
|
||||
expect(isExpanded(type: FolderCategoryType.private), true);
|
||||
expect(isExpanded(type: FolderSpaceType.private), true);
|
||||
|
||||
// collapse the personal folder
|
||||
await tester.tapButton(
|
||||
find.byTooltip(LocaleKeys.sideBar_clickToHidePrivate.tr()),
|
||||
);
|
||||
expect(isExpanded(type: FolderCategoryType.private), false);
|
||||
expect(isExpanded(type: FolderSpaceType.private), false);
|
||||
|
||||
// expand the personal folder
|
||||
await tester.tapButton(
|
||||
find.byTooltip(LocaleKeys.sideBar_clickToHidePrivate.tr()),
|
||||
);
|
||||
expect(isExpanded(type: FolderCategoryType.private), true);
|
||||
expect(isExpanded(type: FolderSpaceType.private), true);
|
||||
});
|
||||
});
|
||||
}
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
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/sidebar/favorites/favorite_folder.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
@@ -46,7 +46,7 @@ void main() {
|
||||
await tester.favoriteViewByName(names[1]);
|
||||
expect(
|
||||
tester.findFavoritePageName(names[1]),
|
||||
findsNWidgets(2),
|
||||
findsNWidgets(1),
|
||||
);
|
||||
|
||||
await tester.unfavoriteViewByName(gettingStarted);
|
||||
@@ -120,9 +120,9 @@ void main() {
|
||||
(widget) =>
|
||||
widget is SingleInnerViewItem &&
|
||||
widget.view.isFavorite &&
|
||||
widget.categoryType == FolderCategoryType.favorite,
|
||||
widget.spaceType == FolderSpaceType.favorite,
|
||||
),
|
||||
findsNWidgets(6),
|
||||
findsNWidgets(3),
|
||||
);
|
||||
|
||||
await tester.hoverOnPageName(
|
||||
@@ -135,7 +135,7 @@ void main() {
|
||||
|
||||
expect(
|
||||
tester.findAllFavoritePages(),
|
||||
findsNWidgets(3),
|
||||
findsNWidgets(2),
|
||||
);
|
||||
|
||||
await tester.hoverOnPageName(
|
||||
@@ -168,7 +168,7 @@ void main() {
|
||||
widget.isSelected != null &&
|
||||
widget.isSelected!(),
|
||||
),
|
||||
findsNWidgets(2),
|
||||
findsNWidgets(1),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -18,6 +18,9 @@ void main() {
|
||||
|
||||
// create document, board, grid and calendar views
|
||||
for (final value in ViewLayoutPB.values) {
|
||||
if (value == ViewLayoutPB.Chat) {
|
||||
continue;
|
||||
}
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: value.name,
|
||||
parentName: gettingStarted,
|
||||
@@ -46,6 +49,9 @@ void main() {
|
||||
|
||||
// create document, board, grid and calendar views
|
||||
for (final value in ViewLayoutPB.values) {
|
||||
if (value == ViewLayoutPB.Chat) {
|
||||
continue;
|
||||
}
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: value.name,
|
||||
parentName: gettingStarted,
|
||||
|
||||
@@ -39,6 +39,9 @@ void main() {
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
for (final layout in ViewLayoutPB.values) {
|
||||
if (layout == ViewLayoutPB.Chat) {
|
||||
continue;
|
||||
}
|
||||
// create a new page
|
||||
final name = 'AppFlowy_$layout';
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
@@ -66,6 +69,8 @@ void main() {
|
||||
case ViewLayoutPB.Calendar:
|
||||
expect(find.byType(CalendarPage), findsOneWidget);
|
||||
break;
|
||||
case ViewLayoutPB.Chat:
|
||||
break;
|
||||
}
|
||||
|
||||
await tester.openPage(gettingStarted);
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'sidebar_favorites_test.dart' as sidebar_favorite_test;
|
||||
import 'sidebar_icon_test.dart' as sidebar_icon_test;
|
||||
import 'sidebar_test.dart' as sidebar_test;
|
||||
|
||||
void startTesting() {
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Sidebar integration tests
|
||||
|
||||
@@ -8,9 +8,10 @@ void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('Empty', () {
|
||||
testWidgets('toggle theme mode', (tester) async {
|
||||
testWidgets('empty test', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.wait(1000);
|
||||
});
|
||||
});
|
||||
}
|
||||
+2
-2
@@ -82,11 +82,11 @@ void main() {
|
||||
HeadingBlockKeys.type,
|
||||
);
|
||||
expect(
|
||||
importedPageEditorState.getNodeAtPath([2])!.type,
|
||||
importedPageEditorState.getNodeAtPath([1])!.type,
|
||||
HeadingBlockKeys.type,
|
||||
);
|
||||
expect(
|
||||
importedPageEditorState.getNodeAtPath([4])!.type,
|
||||
importedPageEditorState.getNodeAtPath([2])!.type,
|
||||
TableBlockKeys.type,
|
||||
);
|
||||
});
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/openai_client.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/ai_client.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_editor/src/render/toolbar/toolbar_widget.dart';
|
||||
@@ -103,8 +103,8 @@ Future<AppFlowyEditor> setUpOpenAITesting(WidgetTester tester) async {
|
||||
}
|
||||
|
||||
Future<void> mockOpenAIRepository() async {
|
||||
await getIt.unregister<OpenAIRepository>();
|
||||
getIt.registerFactoryAsync<OpenAIRepository>(
|
||||
await getIt.unregister<AIRepository>();
|
||||
getIt.registerFactoryAsync<AIRepository>(
|
||||
() => Future.value(
|
||||
MockOpenAIRepository(),
|
||||
),
|
||||
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/plugins/document/presentation/share/share_button.dart';
|
||||
import 'package:appflowy/plugins/shared/share/share_button.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
@@ -51,13 +51,13 @@ void main() {
|
||||
},
|
||||
);
|
||||
|
||||
final shareButton = find.byType(ShareActionList);
|
||||
final shareButtonState =
|
||||
tester.state(shareButton) as ShareActionListState;
|
||||
final shareButton = find.byType(ShareButton);
|
||||
final shareButtonState = tester.widget(shareButton) as ShareButton;
|
||||
|
||||
final path = await mockSaveFilePath(
|
||||
p.join(
|
||||
context.applicationDataDirectory,
|
||||
'${shareButtonState.name}.md',
|
||||
'${shareButtonState.view.name}.md',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ Future<void> runIntegration3OnDesktop() async {
|
||||
settings_test_runner.main();
|
||||
share_markdown_test.main();
|
||||
import_files_test.main();
|
||||
sidebar_test_runner.startTesting();
|
||||
board_test_runner.startTesting();
|
||||
sidebar_test_runner.main();
|
||||
board_test_runner.main();
|
||||
tabs_test.main();
|
||||
}
|
||||
@@ -1,18 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/pages/settings_account_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/setting_supabase_cloud.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'util.dart';
|
||||
|
||||
extension AppFlowyAuthTest on WidgetTester {
|
||||
Future<void> tapGoogleLoginInButton() async {
|
||||
await tapButton(find.byKey(const Key('signInWithGoogleButton')));
|
||||
await tapButton(
|
||||
find.byKey(const Key('signInWithGoogleButton')),
|
||||
);
|
||||
}
|
||||
|
||||
/// Requires being on the SettingsPage.account of the SettingsDialog
|
||||
@@ -26,8 +28,8 @@ extension AppFlowyAuthTest on WidgetTester {
|
||||
|
||||
await tapButton(find.byType(SignInOutButton));
|
||||
|
||||
expectToSeeText(LocaleKeys.button_confirm.tr());
|
||||
await tapButtonWithName(LocaleKeys.button_confirm.tr());
|
||||
expectToSeeText(LocaleKeys.button_ok.tr());
|
||||
await tapButtonWithName(LocaleKeys.button_ok.tr());
|
||||
}
|
||||
|
||||
Future<void> tapSignInAsGuest() async {
|
||||
@@ -44,6 +46,12 @@ extension AppFlowyAuthTest on WidgetTester {
|
||||
assert(isSwitched == value);
|
||||
}
|
||||
|
||||
void assertToggleValue(Finder finder, bool value) {
|
||||
final Toggle switchWidget = widget(finder);
|
||||
final isSwitched = switchWidget.value;
|
||||
assert(isSwitched == value);
|
||||
}
|
||||
|
||||
void assertEnableEncryptSwitchValue(bool value) {
|
||||
assertSwitchValue(
|
||||
find.descendant(
|
||||
@@ -65,10 +73,10 @@ extension AppFlowyAuthTest on WidgetTester {
|
||||
}
|
||||
|
||||
void assertAppFlowyCloudEnableSyncSwitchValue(bool value) {
|
||||
assertSwitchValue(
|
||||
assertToggleValue(
|
||||
find.descendant(
|
||||
of: find.byType(AppFlowyCloudEnableSync),
|
||||
matching: find.byWidgetPredicate((widget) => widget is Switch),
|
||||
matching: find.byWidgetPredicate((widget) => widget is Toggle),
|
||||
),
|
||||
value,
|
||||
);
|
||||
@@ -86,7 +94,7 @@ extension AppFlowyAuthTest on WidgetTester {
|
||||
Future<void> toggleEnableSync(Type syncButton) async {
|
||||
final finder = find.descendant(
|
||||
of: find.byType(syncButton),
|
||||
matching: find.byWidgetPredicate((widget) => widget is Switch),
|
||||
matching: find.byWidgetPredicate((widget) => widget is Toggle),
|
||||
);
|
||||
|
||||
await tapButton(finder);
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy/core/config/kv.dart';
|
||||
import 'package:appflowy/core/config/kv_keys.dart';
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/presentation.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/emoji_picker_button.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/share/share_button.dart';
|
||||
import 'package:appflowy/plugins/shared/share/share_button.dart';
|
||||
import 'package:appflowy/shared/feature_flags.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/presentation/screens/screens.dart';
|
||||
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_new_page_button.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_workspace.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/shared/sidebar_new_page_button.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/sidebar_workspace.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';
|
||||
@@ -27,6 +22,7 @@ import 'package:appflowy/workspace/presentation/notifications/widgets/flowy_tab.
|
||||
import 'package:appflowy/workspace/presentation/notifications/widgets/notification_button.dart';
|
||||
import 'package:appflowy/workspace/presentation/notifications/widgets/notification_tab_bar.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_body.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/more_view_actions/more_view_actions.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/more_view_actions/widgets/common_view_action.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/view_title_bar.dart';
|
||||
@@ -34,6 +30,10 @@ import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/widget/buttons/primary_button.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'emoji.dart';
|
||||
@@ -60,6 +60,7 @@ extension CommonOperations on WidgetTester {
|
||||
/// Tap the + button on the home page.
|
||||
Future<void> tapAddViewButton({
|
||||
String name = gettingStarted,
|
||||
ViewLayoutPB layout = ViewLayoutPB.Document,
|
||||
}) async {
|
||||
await hoverOnPageName(
|
||||
name,
|
||||
@@ -257,7 +258,7 @@ extension CommonOperations on WidgetTester {
|
||||
/// Tap the share button above the document page.
|
||||
Future<void> tapShareButton() async {
|
||||
final shareButton = find.byWidgetPredicate(
|
||||
(widget) => widget is DocumentShareButton,
|
||||
(widget) => widget is ShareButton,
|
||||
);
|
||||
await tapButton(shareButton);
|
||||
}
|
||||
@@ -279,7 +280,7 @@ extension CommonOperations on WidgetTester {
|
||||
bool openAfterCreated = true,
|
||||
}) async {
|
||||
// create a new page
|
||||
await tapAddViewButton(name: parentName ?? gettingStarted);
|
||||
await tapAddViewButton(name: parentName ?? gettingStarted, layout: layout);
|
||||
await tapButtonWithName(layout.menuName);
|
||||
final settingsOrFailure = await getIt<KeyValueStorage>().getWithFormat(
|
||||
KVKeys.showRenameDialogWhenCreatingNewFile,
|
||||
@@ -619,6 +620,18 @@ extension SettingsFinder on CommonFinders {
|
||||
matching: find.byType(Scrollable),
|
||||
)
|
||||
.first;
|
||||
|
||||
Finder findSettingsMenuScrollable() => find
|
||||
.descendant(
|
||||
of: find
|
||||
.descendant(
|
||||
of: find.byType(SettingsMenu),
|
||||
matching: find.byType(SingleChildScrollView),
|
||||
)
|
||||
.first,
|
||||
matching: find.byType(Scrollable),
|
||||
)
|
||||
.first;
|
||||
}
|
||||
|
||||
extension ViewLayoutPBTest on ViewLayoutPB {
|
||||
|
||||
@@ -130,12 +130,12 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await openPage('v020', layout: ViewLayoutPB.Grid);
|
||||
}
|
||||
|
||||
Future<void> hoverOnFirstRowOfGrid() async {
|
||||
Future<void> hoverOnFirstRowOfGrid([Future<void> Function()? onHover]) async {
|
||||
final findRow = find.byType(GridRow);
|
||||
expect(findRow, findsWidgets);
|
||||
|
||||
final firstRow = findRow.first;
|
||||
await hoverOnWidget(firstRow);
|
||||
await hoverOnWidget(firstRow, onHover: onHover);
|
||||
}
|
||||
|
||||
Future<void> editCell({
|
||||
@@ -876,11 +876,13 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
|
||||
Future<void> tapRowMenuButtonInGrid() async {
|
||||
expect(find.byType(RowMenuButton), findsOneWidget);
|
||||
await tapButton(find.byType(RowMenuButton));
|
||||
}
|
||||
|
||||
/// Should call [tapRowMenuButtonInGrid] first.
|
||||
Future<void> tapDeleteOnRowMenu() async {
|
||||
expect(find.text(LocaleKeys.grid_row_delete.tr()), findsOneWidget);
|
||||
await tapButtonWithName(LocaleKeys.grid_row_delete.tr());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import 'dart:async';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/base/emoji/emoji_picker.dart';
|
||||
import 'package:appflowy/plugins/base/emoji/emoji_skin_tone.dart';
|
||||
@@ -10,12 +13,10 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/bl
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/cover_editor.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/plugins/document/presentation/editor_plugins/image/embed_image_url_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu/widgets/embed_image_url_widget.dart';
|
||||
import 'package:appflowy/plugins/inline_actions/widgets/inline_actions_handler.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_emoji_mart/flutter_emoji_mart.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
@@ -81,15 +82,12 @@ class EditorOperations {
|
||||
|
||||
/// Taps the 'Remove Icon' button in the cover toolbar and the icon popover
|
||||
Future<void> tapRemoveIconButton({bool isInPicker = false}) async {
|
||||
Finder button =
|
||||
find.text(LocaleKeys.document_plugins_cover_removeIcon.tr());
|
||||
if (isInPicker) {
|
||||
button = find.descendant(
|
||||
of: find.byType(FlowyIconPicker),
|
||||
matching: button,
|
||||
);
|
||||
}
|
||||
|
||||
final Finder button = !isInPicker
|
||||
? find.text(LocaleKeys.document_plugins_cover_removeIcon.tr())
|
||||
: find.descendant(
|
||||
of: find.byType(FlowyIconPicker),
|
||||
matching: find.text(LocaleKeys.button_remove.tr()),
|
||||
);
|
||||
await tester.tapButton(button);
|
||||
}
|
||||
|
||||
|
||||
@@ -165,7 +165,7 @@ extension Expectation on WidgetTester {
|
||||
(widget) =>
|
||||
widget is SingleInnerViewItem &&
|
||||
widget.view.isFavorite &&
|
||||
widget.categoryType == FolderCategoryType.favorite &&
|
||||
widget.spaceType == FolderSpaceType.favorite &&
|
||||
widget.view.name == name &&
|
||||
widget.view.layout == layout,
|
||||
skipOffstage: false,
|
||||
@@ -175,7 +175,7 @@ extension Expectation on WidgetTester {
|
||||
(widget) =>
|
||||
widget is SingleInnerViewItem &&
|
||||
widget.view.isFavorite &&
|
||||
widget.categoryType == FolderCategoryType.favorite,
|
||||
widget.spaceType == FolderSpaceType.favorite,
|
||||
);
|
||||
|
||||
Finder findPageName(
|
||||
|
||||
@@ -5,10 +5,18 @@ class FlowyTestKeyboard {
|
||||
static Future<void> simulateKeyDownEvent(
|
||||
List<LogicalKeyboardKey> keys, {
|
||||
required flutter_test.WidgetTester tester,
|
||||
bool withKeyUp = false,
|
||||
}) async {
|
||||
for (final LogicalKeyboardKey key in keys) {
|
||||
await flutter_test.simulateKeyDownEvent(key);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
if (withKeyUp) {
|
||||
for (final LogicalKeyboardKey key in keys) {
|
||||
await flutter_test.simulateKeyUpEvent(key);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ class MockOpenAIRepository extends HttpOpenAIRepository {
|
||||
required Future<void> Function() onStart,
|
||||
required Future<void> Function(TextCompletionResponse response) onProcess,
|
||||
required Future<void> Function() onEnd,
|
||||
required void Function(OpenAIError error) onError,
|
||||
required void Function(AIError error) onError,
|
||||
String? suffix,
|
||||
int maxTokens = 2048,
|
||||
double temperature = 0.3,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_setting.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/shared/sidebar_setting.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/pages/settings_account_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/pages/settings_workspace_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/settings_dialog.dart';
|
||||
@@ -32,6 +32,14 @@ extension AppFlowySettings on WidgetTester {
|
||||
final button = find.byWidgetPredicate(
|
||||
(widget) => widget is SettingsMenuElement && widget.page == page,
|
||||
);
|
||||
|
||||
await scrollUntilVisible(
|
||||
button,
|
||||
0,
|
||||
scrollable: find.findSettingsMenuScrollable(),
|
||||
);
|
||||
await pump();
|
||||
|
||||
expect(button, findsOneWidget);
|
||||
await tapButton(button);
|
||||
return;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_workspace.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_actions.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_icon.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/sidebar_workspace.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'base.dart';
|
||||
import 'util.dart';
|
||||
|
||||
extension AppFlowyWorkspace on WidgetTester {
|
||||
/// Open workspace menu
|
||||
@@ -36,12 +36,19 @@ extension AppFlowyWorkspace on WidgetTester {
|
||||
matching: find.byType(WorkspaceMoreActionList),
|
||||
);
|
||||
expect(moreButton, findsOneWidget);
|
||||
await tapButton(moreButton);
|
||||
await tapButton(find.findTextInFlowyText(LocaleKeys.button_rename.tr()));
|
||||
final input = find.byType(TextFormField);
|
||||
expect(input, findsOneWidget);
|
||||
await enterText(input, name);
|
||||
await tapButton(find.text(LocaleKeys.button_ok.tr()));
|
||||
await hoverOnWidget(
|
||||
moreButton,
|
||||
onHover: () async {
|
||||
await tapButton(moreButton);
|
||||
await tapButton(
|
||||
find.findTextInFlowyText(LocaleKeys.button_rename.tr()),
|
||||
);
|
||||
final input = find.byType(TextFormField);
|
||||
expect(input, findsOneWidget);
|
||||
await enterText(input, name);
|
||||
await tapButton(find.text(LocaleKeys.button_ok.tr()));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> changeWorkspaceIcon(String icon) async {
|
||||
|
||||
@@ -65,6 +65,8 @@ PODS:
|
||||
- FlutterMacOS
|
||||
- permission_handler_apple (9.3.0):
|
||||
- Flutter
|
||||
- printing (1.0.0):
|
||||
- Flutter
|
||||
- ReachabilitySwift (5.0.0)
|
||||
- SDWebImage (5.14.2):
|
||||
- SDWebImage/Core (= 5.14.2)
|
||||
@@ -101,6 +103,7 @@ DEPENDENCIES:
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||
- printing (from `.symlinks/plugins/printing/ios`)
|
||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
|
||||
@@ -149,6 +152,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||
permission_handler_apple:
|
||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||
printing:
|
||||
:path: ".symlinks/plugins/printing/ios"
|
||||
share_plus:
|
||||
:path: ".symlinks/plugins/share_plus/ios"
|
||||
shared_preferences_foundation:
|
||||
@@ -173,12 +178,13 @@ SPEC CHECKSUMS:
|
||||
fluttertoast: 31b00dabfa7fb7bacd9e7dbee580d7a2ff4bf265
|
||||
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
|
||||
image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425
|
||||
integration_test: 13825b8a9334a850581300559b8839134b124670
|
||||
integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4
|
||||
irondash_engine_context: 3458bf979b90d616ffb8ae03a150bafe2e860cc9
|
||||
keyboard_height_plugin: 43fa8bba20fd5c4fdeed5076466b8b9d43cc6b86
|
||||
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
|
||||
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
|
||||
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
||||
printing: 233e1b73bd1f4a05615548e9b5a324c98588640b
|
||||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||
SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84
|
||||
share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5
|
||||
|
||||
@@ -66,5 +66,10 @@
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false />
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -18,6 +18,13 @@ class KVKeys {
|
||||
/// {'dx': 10.0, 'dy': 10.0}
|
||||
static const String windowPosition = 'windowPosition';
|
||||
|
||||
/// The key for saving the window status
|
||||
///
|
||||
/// The value is a json string with the following format:
|
||||
/// { 'windowMaximized': true }
|
||||
///
|
||||
static const String windowMaximized = 'windowMaximized';
|
||||
|
||||
static const String kDocumentAppearanceFontSize =
|
||||
'kDocumentAppearanceFontSize';
|
||||
static const String kDocumentAppearanceFontFamily =
|
||||
@@ -65,6 +72,11 @@ class KVKeys {
|
||||
/// {'feature_flag_1': true, 'feature_flag_2': false}
|
||||
static const String featureFlag = 'featureFlag';
|
||||
|
||||
/// The key for saving show notification icon option
|
||||
///
|
||||
/// The value is a boolean string
|
||||
static const String showNotificationIcon = 'showNotificationIcon';
|
||||
|
||||
/// The key for saving the last opened workspace id
|
||||
///
|
||||
/// The workspace id is a string.
|
||||
@@ -75,4 +87,25 @@ class KVKeys {
|
||||
///
|
||||
/// The value is a double string.
|
||||
static const String scaleFactor = 'scaleFactor';
|
||||
|
||||
/// The key for saving the last opened tab (favorite, recent, space etc.)
|
||||
///
|
||||
/// The value is a int string.
|
||||
static const String lastOpenedSpace = 'lastOpenedSpace';
|
||||
|
||||
/// The key for saving the space tab order
|
||||
///
|
||||
/// The value is a json string with the following format:
|
||||
/// [0, 1, 2]
|
||||
static const String spaceOrder = 'spaceOrder';
|
||||
|
||||
/// The key for saving the last opened space id (space A, space B)
|
||||
///
|
||||
/// The value is a string.
|
||||
static const String lastOpenedSpaceId = 'lastOpenedSpaceId';
|
||||
|
||||
/// The key for saving the upgrade space tag
|
||||
///
|
||||
/// The value is a boolean string
|
||||
static const String hasUpgradedSpace = 'hasUpgradedSpace060';
|
||||
}
|
||||
@@ -5,10 +5,10 @@ import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/shared/window_title_bar.dart';
|
||||
import 'package:appflowy/workspace/application/home/home_setting_bloc.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class CocoaWindowChannel {
|
||||
@@ -102,22 +102,38 @@ class MoveWindowDetectorState extends State<MoveWindowDetector> {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final textSpan = TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '${LocaleKeys.sideBar_openSidebar.tr()}\n',
|
||||
style: context.tooltipTextStyle(),
|
||||
),
|
||||
TextSpan(
|
||||
text: Platform.isMacOS ? '⌘+.' : 'Ctrl+\\',
|
||||
style: context
|
||||
.tooltipTextStyle()
|
||||
?.copyWith(color: Theme.of(context).hintColor),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return FlowyTooltip(
|
||||
richMessage: TextSpan(
|
||||
children: [
|
||||
TextSpan(text: '${LocaleKeys.sideBar_closeSidebar.tr()}\n'),
|
||||
const TextSpan(text: 'Ctrl+\\'),
|
||||
],
|
||||
),
|
||||
child: FlowyIconButton(
|
||||
hoverColor: Colors.transparent,
|
||||
onPressed: () => context
|
||||
richMessage: textSpan,
|
||||
child: Listener(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onPointerDown: (_) => context
|
||||
.read<HomeSettingBloc>()
|
||||
.add(const HomeSettingEvent.collapseMenu()),
|
||||
iconPadding: const EdgeInsets.fromLTRB(4, 4, 4, 4),
|
||||
icon: context.read<HomeSettingBloc>().state.isMenuCollapsed
|
||||
? const FlowySvg(FlowySvgs.show_menu_s)
|
||||
: const FlowySvg(FlowySvgs.hide_menu_m),
|
||||
child: FlowyHover(
|
||||
child: Container(
|
||||
width: 24,
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: const RotatedBox(
|
||||
quarterTurns: 2,
|
||||
child: FlowySvg(FlowySvgs.hide_menu_s),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:string_validator/string_validator.dart';
|
||||
import 'package:url_launcher/url_launcher.dart' as launcher;
|
||||
|
||||
|
||||
+7
@@ -29,4 +29,11 @@ abstract class Env {
|
||||
defaultValue: '',
|
||||
)
|
||||
static const String afCloudUrl = _Env.afCloudUrl;
|
||||
|
||||
@EnviedField(
|
||||
obfuscate: false,
|
||||
varName: 'INTERNAL_BUILD',
|
||||
defaultValue: '',
|
||||
)
|
||||
static const String internalBuild = _Env.internalBuild;
|
||||
}
|
||||
@@ -481,7 +481,7 @@ class _AFDropdownMenuState<T> extends State<AFDropdownMenu<T>> {
|
||||
|
||||
ButtonStyle effectiveStyle = entry.style ?? defaultStyle;
|
||||
final Color focusedBackgroundColor = effectiveStyle.foregroundColor
|
||||
?.resolve(<MaterialState>{MaterialState.focused}) ??
|
||||
?.resolve(<WidgetState>{WidgetState.focused}) ??
|
||||
Theme.of(context).colorScheme.onSurface;
|
||||
|
||||
Widget label = entry.labelWidget ?? Text(entry.label);
|
||||
@@ -499,7 +499,7 @@ class _AFDropdownMenuState<T> extends State<AFDropdownMenu<T>> {
|
||||
// color will also change to foregroundColor.withOpacity(0.12).
|
||||
effectiveStyle = entry.enabled && i == focusedIndex
|
||||
? effectiveStyle.copyWith(
|
||||
backgroundColor: MaterialStatePropertyAll<Color>(
|
||||
backgroundColor: WidgetStatePropertyAll<Color>(
|
||||
focusedBackgroundColor.withOpacity(0.12),
|
||||
),
|
||||
)
|
||||
@@ -628,17 +628,17 @@ class _AFDropdownMenuState<T> extends State<AFDropdownMenu<T>> {
|
||||
final double? anchorWidth = getWidth(_anchorKey);
|
||||
if (widget.width != null) {
|
||||
effectiveMenuStyle = effectiveMenuStyle.copyWith(
|
||||
minimumSize: MaterialStatePropertyAll<Size?>(Size(widget.width!, 0.0)),
|
||||
minimumSize: WidgetStatePropertyAll<Size?>(Size(widget.width!, 0.0)),
|
||||
);
|
||||
} else if (anchorWidth != null) {
|
||||
effectiveMenuStyle = effectiveMenuStyle.copyWith(
|
||||
minimumSize: MaterialStatePropertyAll<Size?>(Size(anchorWidth, 0.0)),
|
||||
minimumSize: WidgetStatePropertyAll<Size?>(Size(anchorWidth, 0.0)),
|
||||
);
|
||||
}
|
||||
|
||||
if (widget.menuHeight != null) {
|
||||
effectiveMenuStyle = effectiveMenuStyle.copyWith(
|
||||
maximumSize: MaterialStatePropertyAll<Size>(
|
||||
maximumSize: WidgetStatePropertyAll<Size>(
|
||||
Size(double.infinity, widget.menuHeight!),
|
||||
),
|
||||
);
|
||||
@@ -1029,8 +1029,8 @@ class _DropdownMenuDefaultsM3 extends DropdownMenuThemeData {
|
||||
@override
|
||||
MenuStyle get menuStyle {
|
||||
return const MenuStyle(
|
||||
minimumSize: MaterialStatePropertyAll<Size>(Size(_kMinimumWidth, 0.0)),
|
||||
maximumSize: MaterialStatePropertyAll<Size>(Size.infinite),
|
||||
minimumSize: WidgetStatePropertyAll<Size>(Size(_kMinimumWidth, 0.0)),
|
||||
maximumSize: WidgetStatePropertyAll<Size>(Size.infinite),
|
||||
visualDensity: VisualDensity.standard,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:appflowy/mobile/presentation/chat/mobile_chat_screen.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -14,15 +16,15 @@ import 'package:go_router/go_router.dart';
|
||||
|
||||
extension MobileRouter on BuildContext {
|
||||
Future<void> pushView(ViewPB view, [Map<String, dynamic>? arguments]) async {
|
||||
await push(
|
||||
Uri(
|
||||
path: view.routeName,
|
||||
queryParameters: view.queryParameters(arguments),
|
||||
).toString(),
|
||||
).then((value) {
|
||||
getIt<MenuSharedState>().latestOpenView = view;
|
||||
getIt<CachedRecentService>().updateRecentViews([view.id], true);
|
||||
});
|
||||
// set the current view before pushing the new view
|
||||
getIt<MenuSharedState>().latestOpenView = view;
|
||||
unawaited(getIt<CachedRecentService>().updateRecentViews([view.id], true));
|
||||
|
||||
final uri = Uri(
|
||||
path: view.routeName,
|
||||
queryParameters: view.queryParameters(arguments),
|
||||
).toString();
|
||||
await push(uri);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +39,9 @@ extension on ViewPB {
|
||||
return MobileCalendarScreen.routeName;
|
||||
case ViewLayoutPB.Board:
|
||||
return MobileBoardScreen.routeName;
|
||||
case ViewLayoutPB.Chat:
|
||||
return MobileChatScreen.routeName;
|
||||
|
||||
default:
|
||||
throw UnimplementedError('routeName for $this is not implemented');
|
||||
}
|
||||
@@ -65,6 +70,11 @@ extension on ViewPB {
|
||||
MobileBoardScreen.viewId: id,
|
||||
MobileBoardScreen.viewTitle: name,
|
||||
};
|
||||
case ViewLayoutPB.Chat:
|
||||
return {
|
||||
MobileChatScreen.viewId: id,
|
||||
MobileChatScreen.viewTitle: name,
|
||||
};
|
||||
default:
|
||||
throw UnimplementedError(
|
||||
'queryParameters for $this is not implemented',
|
||||
|
||||
@@ -57,7 +57,19 @@ class RecentViewBloc extends Bloc<RecentViewEvent, RecentViewState> {
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// only document supports the cover
|
||||
if (view.layout != ViewLayoutPB.Document) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
name: view.name,
|
||||
icon: view.icon.value,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final cover = getCoverV2();
|
||||
|
||||
if (cover != null) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
|
||||
@@ -6,8 +6,8 @@ import 'package:appflowy/mobile/presentation/base/view_page/app_bar_buttons.dart
|
||||
import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_state_container.dart';
|
||||
import 'package:appflowy/plugins/base/emoji/emoji_text.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/document_collaborators.dart';
|
||||
import 'package:appflowy/plugins/shared/sync_indicator.dart';
|
||||
import 'package:appflowy/shared/feature_flags.dart';
|
||||
import 'package:appflowy/startup/plugin/plugin.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/application/reminder/reminder_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||
@@ -125,7 +125,7 @@ class _MobileViewPageState extends State<MobileViewPage> {
|
||||
return child;
|
||||
},
|
||||
)
|
||||
: child;
|
||||
: SafeArea(child: child);
|
||||
return Scaffold(
|
||||
extendBodyBehindAppBar: isDocument,
|
||||
appBar: appBar,
|
||||
@@ -154,7 +154,10 @@ class _MobileViewPageState extends State<MobileViewPage> {
|
||||
(view) {
|
||||
final plugin = view.plugin(arguments: widget.arguments ?? const {})
|
||||
..init();
|
||||
return plugin.widgetBuilder.buildWidget(shrinkWrap: false);
|
||||
return plugin.widgetBuilder.buildWidget(
|
||||
shrinkWrap: false,
|
||||
context: PluginContext(userProfile: state.userProfilePB),
|
||||
);
|
||||
},
|
||||
(error) {
|
||||
return FlowyMobileStateContainer.error(
|
||||
@@ -191,13 +194,6 @@ class _MobileViewPageState extends State<MobileViewPage> {
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
view: view,
|
||||
),
|
||||
const HSpace(16.0),
|
||||
DocumentSyncIndicator(view: view),
|
||||
const HSpace(12.0),
|
||||
]);
|
||||
} else {
|
||||
actions.addAll([
|
||||
DatabaseSyncIndicator(view: view),
|
||||
const HSpace(12.0),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart' hide WidgetBuilder;
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class TypeOptionMenuItemValue<T> {
|
||||
|
||||
+3
-2
@@ -12,6 +12,7 @@ import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/view/prelude.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@@ -92,7 +93,7 @@ class MobileViewPageMoreButton extends StatelessWidget {
|
||||
context,
|
||||
showDragHandle: true,
|
||||
showDivider: false,
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
backgroundColor: AFThemeExtension.of(context).background,
|
||||
builder: (_) => MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider.value(value: context.read<ViewBloc>()),
|
||||
@@ -144,7 +145,7 @@ class MobileViewPageLayoutButton extends StatelessWidget {
|
||||
showDoneButton: true,
|
||||
showHeader: true,
|
||||
title: LocaleKeys.pageStyle_title.tr(),
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
backgroundColor: AFThemeExtension.of(context).background,
|
||||
builder: (_) => MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider.value(value: context.read<DocumentPageStyleBloc>()),
|
||||
|
||||
+1
@@ -38,6 +38,7 @@ class MobileViewPageMoreBottomSheet extends StatelessWidget {
|
||||
case MobileViewBottomSheetBodyAction.removeFromFavorites:
|
||||
context.pop();
|
||||
context.read<FavoriteBloc>().add(FavoriteEvent.toggle(view));
|
||||
|
||||
break;
|
||||
case MobileViewBottomSheetBodyAction.undo:
|
||||
EditorNotification.undo().post();
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -19,7 +20,7 @@ class BottomSheetActionWidget extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final iconColor =
|
||||
this.iconColor ?? Theme.of(context).colorScheme.onBackground;
|
||||
this.iconColor ?? AFThemeExtension.of(context).onBackground;
|
||||
|
||||
if (svg == null) {
|
||||
return OutlinedButton(
|
||||
|
||||
+22
-8
@@ -21,40 +21,54 @@ class AddNewPageWidgetBottomSheet extends StatelessWidget {
|
||||
children: [
|
||||
FlowyOptionTile.text(
|
||||
text: LocaleKeys.document_menuName.tr(),
|
||||
height: 52.0,
|
||||
leftIcon: const FlowySvg(
|
||||
FlowySvgs.document_s,
|
||||
size: Size.square(20),
|
||||
FlowySvgs.icon_document_s,
|
||||
size: Size.square(18),
|
||||
),
|
||||
showTopBorder: false,
|
||||
onTap: () => onAction(ViewLayoutPB.Document),
|
||||
),
|
||||
FlowyOptionTile.text(
|
||||
text: LocaleKeys.grid_menuName.tr(),
|
||||
height: 52.0,
|
||||
leftIcon: const FlowySvg(
|
||||
FlowySvgs.grid_s,
|
||||
size: Size.square(20),
|
||||
FlowySvgs.icon_grid_s,
|
||||
size: Size.square(18),
|
||||
),
|
||||
showTopBorder: false,
|
||||
onTap: () => onAction(ViewLayoutPB.Grid),
|
||||
),
|
||||
FlowyOptionTile.text(
|
||||
text: LocaleKeys.board_menuName.tr(),
|
||||
height: 52.0,
|
||||
leftIcon: const FlowySvg(
|
||||
FlowySvgs.board_s,
|
||||
size: Size.square(20),
|
||||
FlowySvgs.icon_board_s,
|
||||
size: Size.square(18),
|
||||
),
|
||||
showTopBorder: false,
|
||||
onTap: () => onAction(ViewLayoutPB.Board),
|
||||
),
|
||||
FlowyOptionTile.text(
|
||||
text: LocaleKeys.calendar_menuName.tr(),
|
||||
height: 52.0,
|
||||
leftIcon: const FlowySvg(
|
||||
FlowySvgs.date_s,
|
||||
size: Size.square(20),
|
||||
FlowySvgs.icon_calendar_s,
|
||||
size: Size.square(18),
|
||||
),
|
||||
showTopBorder: false,
|
||||
onTap: () => onAction(ViewLayoutPB.Calendar),
|
||||
),
|
||||
FlowyOptionTile.text(
|
||||
text: LocaleKeys.chat_newChat.tr(),
|
||||
height: 52.0,
|
||||
leftIcon: const FlowySvg(
|
||||
FlowySvgs.chat_ai_page_s,
|
||||
size: Size.square(18),
|
||||
),
|
||||
showTopBorder: false,
|
||||
onTap: () => onAction(ViewLayoutPB.Chat),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
+2
-1
@@ -33,6 +33,7 @@ class BlockActionBottomSheet extends StatelessWidget {
|
||||
FlowySvgs.arrow_up_s,
|
||||
size: Size.square(20),
|
||||
),
|
||||
showTopBorder: false,
|
||||
onTap: () => onAction(BlockActionBottomSheetType.insertAbove),
|
||||
),
|
||||
FlowyOptionTile.text(
|
||||
@@ -48,7 +49,7 @@ class BlockActionBottomSheet extends StatelessWidget {
|
||||
FlowyOptionTile.text(
|
||||
showTopBorder: false,
|
||||
text: LocaleKeys.button_duplicate.tr(),
|
||||
leftIcon: const FlowySvg(FlowySvgs.m_field_copy_s),
|
||||
leftIcon: const FlowySvg(FlowySvgs.m_duplicate_s),
|
||||
onTap: () => onAction(BlockActionBottomSheetType.duplicate),
|
||||
),
|
||||
|
||||
|
||||
+88
-1
@@ -1,9 +1,17 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/show_flowy_mobile_confirm_dialog.dart';
|
||||
import 'package:appflowy/startup/tasks/app_widget.dart';
|
||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/recent/recent_views_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
|
||||
enum MobileBottomSheetType {
|
||||
view,
|
||||
@@ -14,11 +22,13 @@ class MobileViewItemBottomSheet extends StatefulWidget {
|
||||
const MobileViewItemBottomSheet({
|
||||
super.key,
|
||||
required this.view,
|
||||
required this.actions,
|
||||
this.defaultType = MobileBottomSheetType.view,
|
||||
});
|
||||
|
||||
final ViewPB view;
|
||||
final MobileBottomSheetType defaultType;
|
||||
final List<MobileViewItemBottomSheetBodyAction> actions;
|
||||
|
||||
@override
|
||||
State<MobileViewItemBottomSheet> createState() =>
|
||||
@@ -27,12 +37,14 @@ class MobileViewItemBottomSheet extends StatefulWidget {
|
||||
|
||||
class _MobileViewItemBottomSheetState extends State<MobileViewItemBottomSheet> {
|
||||
MobileBottomSheetType type = MobileBottomSheetType.view;
|
||||
final fToast = FToast();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
type = widget.defaultType;
|
||||
fToast.init(AppGlobals.context);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -40,6 +52,7 @@ class _MobileViewItemBottomSheetState extends State<MobileViewItemBottomSheet> {
|
||||
switch (type) {
|
||||
case MobileBottomSheetType.view:
|
||||
return MobileViewItemBottomSheetBody(
|
||||
actions: widget.actions,
|
||||
isFavorite: widget.view.isFavorite,
|
||||
onAction: (action) {
|
||||
switch (action) {
|
||||
@@ -59,7 +72,6 @@ class _MobileViewItemBottomSheetState extends State<MobileViewItemBottomSheet> {
|
||||
case MobileViewItemBottomSheetBodyAction.delete:
|
||||
Navigator.pop(context);
|
||||
context.read<ViewBloc>().add(const ViewEvent.delete());
|
||||
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.addToFavorites:
|
||||
case MobileViewItemBottomSheetBodyAction.removeFromFavorites:
|
||||
@@ -68,6 +80,11 @@ class _MobileViewItemBottomSheetState extends State<MobileViewItemBottomSheet> {
|
||||
.read<FavoriteBloc>()
|
||||
.add(FavoriteEvent.toggle(widget.view));
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.removeFromRecent:
|
||||
_removeFromRecent(context);
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.divider:
|
||||
break;
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -83,4 +100,74 @@ class _MobileViewItemBottomSheetState extends State<MobileViewItemBottomSheet> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _removeFromRecent(BuildContext context) async {
|
||||
final viewId = context.read<ViewBloc>().view.id;
|
||||
final recentViewsBloc = context.read<RecentViewsBloc>();
|
||||
Navigator.pop(context);
|
||||
|
||||
await _showConfirmDialog(
|
||||
onDelete: () {
|
||||
recentViewsBloc.add(RecentViewsEvent.removeRecentViews([viewId]));
|
||||
|
||||
fToast.showToast(
|
||||
child: const _RemoveToast(),
|
||||
positionedToastBuilder: (context, child) {
|
||||
return Positioned.fill(
|
||||
top: 450,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _showConfirmDialog({required VoidCallback onDelete}) async {
|
||||
await showFlowyCupertinoConfirmDialog(
|
||||
title: LocaleKeys.sideBar_removePageFromRecent.tr(),
|
||||
leftButton: FlowyText.regular(
|
||||
LocaleKeys.button_cancel.tr(),
|
||||
color: const Color(0xFF1456F0),
|
||||
),
|
||||
rightButton: FlowyText.medium(
|
||||
LocaleKeys.button_delete.tr(),
|
||||
color: const Color(0xFFFE0220),
|
||||
),
|
||||
onRightButtonPressed: (context) {
|
||||
onDelete();
|
||||
Navigator.pop(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _RemoveToast extends StatelessWidget {
|
||||
const _RemoveToast();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 13.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
color: const Color(0xE5171717),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const FlowySvg(
|
||||
FlowySvgs.success_s,
|
||||
blendMode: null,
|
||||
),
|
||||
const HSpace(8.0),
|
||||
FlowyText.regular(
|
||||
LocaleKeys.sideBar_removeSuccess.tr(),
|
||||
fontSize: 16.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+121
-48
@@ -1,6 +1,6 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_quick_action_button.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -11,6 +11,8 @@ enum MobileViewItemBottomSheetBodyAction {
|
||||
delete,
|
||||
addToFavorites,
|
||||
removeFromFavorites,
|
||||
divider,
|
||||
removeFromRecent,
|
||||
}
|
||||
|
||||
class MobileViewItemBottomSheetBody extends StatelessWidget {
|
||||
@@ -18,63 +20,134 @@ class MobileViewItemBottomSheetBody extends StatelessWidget {
|
||||
super.key,
|
||||
this.isFavorite = false,
|
||||
required this.onAction,
|
||||
required this.actions,
|
||||
});
|
||||
|
||||
final bool isFavorite;
|
||||
final void Function(MobileViewItemBottomSheetBodyAction action) onAction;
|
||||
final List<MobileViewItemBottomSheetBodyAction> actions;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
MobileQuickActionButton(
|
||||
text: LocaleKeys.button_rename.tr(),
|
||||
icon: FlowySvgs.m_rename_s,
|
||||
onTap: () => onAction(
|
||||
MobileViewItemBottomSheetBodyAction.rename,
|
||||
),
|
||||
),
|
||||
_divider(),
|
||||
MobileQuickActionButton(
|
||||
text: isFavorite
|
||||
? LocaleKeys.button_removeFromFavorites.tr()
|
||||
: LocaleKeys.button_addToFavorites.tr(),
|
||||
icon: isFavorite
|
||||
? FlowySvgs.m_favorite_selected_lg
|
||||
: FlowySvgs.m_favorite_unselected_lg,
|
||||
iconColor: isFavorite ? Colors.yellow : null,
|
||||
onTap: () => onAction(
|
||||
isFavorite
|
||||
? MobileViewItemBottomSheetBodyAction.removeFromFavorites
|
||||
: MobileViewItemBottomSheetBodyAction.addToFavorites,
|
||||
),
|
||||
),
|
||||
_divider(),
|
||||
MobileQuickActionButton(
|
||||
text: LocaleKeys.button_duplicate.tr(),
|
||||
icon: FlowySvgs.m_duplicate_s,
|
||||
onTap: () => onAction(
|
||||
MobileViewItemBottomSheetBodyAction.duplicate,
|
||||
),
|
||||
),
|
||||
_divider(),
|
||||
MobileQuickActionButton(
|
||||
text: LocaleKeys.button_delete.tr(),
|
||||
textColor: Theme.of(context).colorScheme.error,
|
||||
icon: FlowySvgs.m_delete_s,
|
||||
iconColor: Theme.of(context).colorScheme.error,
|
||||
onTap: () => onAction(
|
||||
MobileViewItemBottomSheetBodyAction.delete,
|
||||
),
|
||||
),
|
||||
_divider(),
|
||||
],
|
||||
children:
|
||||
actions.map((action) => _buildActionButton(context, action)).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _divider() => const Divider(
|
||||
height: 8.5,
|
||||
thickness: 0.5,
|
||||
);
|
||||
Widget _buildActionButton(
|
||||
BuildContext context,
|
||||
MobileViewItemBottomSheetBodyAction action,
|
||||
) {
|
||||
switch (action) {
|
||||
case MobileViewItemBottomSheetBodyAction.rename:
|
||||
return FlowyOptionTile.text(
|
||||
text: LocaleKeys.button_rename.tr(),
|
||||
height: 52.0,
|
||||
leftIcon: const FlowySvg(
|
||||
FlowySvgs.view_item_rename_s,
|
||||
size: Size.square(18),
|
||||
),
|
||||
showTopBorder: false,
|
||||
showBottomBorder: false,
|
||||
onTap: () => onAction(
|
||||
MobileViewItemBottomSheetBodyAction.rename,
|
||||
),
|
||||
);
|
||||
case MobileViewItemBottomSheetBodyAction.duplicate:
|
||||
return FlowyOptionTile.text(
|
||||
text: LocaleKeys.button_duplicate.tr(),
|
||||
height: 52.0,
|
||||
leftIcon: const FlowySvg(
|
||||
FlowySvgs.duplicate_s,
|
||||
size: Size.square(18),
|
||||
),
|
||||
showTopBorder: false,
|
||||
showBottomBorder: false,
|
||||
onTap: () => onAction(
|
||||
MobileViewItemBottomSheetBodyAction.duplicate,
|
||||
),
|
||||
);
|
||||
|
||||
case MobileViewItemBottomSheetBodyAction.share:
|
||||
return FlowyOptionTile.text(
|
||||
text: LocaleKeys.button_share.tr(),
|
||||
height: 52.0,
|
||||
leftIcon: const FlowySvg(
|
||||
FlowySvgs.share_s,
|
||||
size: Size.square(18),
|
||||
),
|
||||
showTopBorder: false,
|
||||
showBottomBorder: false,
|
||||
onTap: () => onAction(
|
||||
MobileViewItemBottomSheetBodyAction.share,
|
||||
),
|
||||
);
|
||||
case MobileViewItemBottomSheetBodyAction.delete:
|
||||
return FlowyOptionTile.text(
|
||||
text: LocaleKeys.button_delete.tr(),
|
||||
height: 52.0,
|
||||
textColor: Theme.of(context).colorScheme.error,
|
||||
leftIcon: FlowySvg(
|
||||
FlowySvgs.trash_s,
|
||||
size: const Size.square(18),
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
showTopBorder: false,
|
||||
showBottomBorder: false,
|
||||
onTap: () => onAction(
|
||||
MobileViewItemBottomSheetBodyAction.delete,
|
||||
),
|
||||
);
|
||||
case MobileViewItemBottomSheetBodyAction.addToFavorites:
|
||||
return FlowyOptionTile.text(
|
||||
height: 52.0,
|
||||
text: LocaleKeys.button_addToFavorites.tr(),
|
||||
leftIcon: const FlowySvg(
|
||||
FlowySvgs.favorite_s,
|
||||
size: Size.square(18),
|
||||
),
|
||||
showTopBorder: false,
|
||||
showBottomBorder: false,
|
||||
onTap: () => onAction(
|
||||
MobileViewItemBottomSheetBodyAction.addToFavorites,
|
||||
),
|
||||
);
|
||||
case MobileViewItemBottomSheetBodyAction.removeFromFavorites:
|
||||
return FlowyOptionTile.text(
|
||||
height: 52.0,
|
||||
text: LocaleKeys.button_removeFromFavorites.tr(),
|
||||
leftIcon: const FlowySvg(
|
||||
FlowySvgs.favorite_section_remove_from_favorite_s,
|
||||
size: Size.square(18),
|
||||
),
|
||||
showTopBorder: false,
|
||||
showBottomBorder: false,
|
||||
onTap: () => onAction(
|
||||
MobileViewItemBottomSheetBodyAction.removeFromFavorites,
|
||||
),
|
||||
);
|
||||
case MobileViewItemBottomSheetBodyAction.removeFromRecent:
|
||||
return FlowyOptionTile.text(
|
||||
height: 52.0,
|
||||
text: LocaleKeys.button_removeFromRecent.tr(),
|
||||
leftIcon: const FlowySvg(
|
||||
FlowySvgs.remove_from_recent_s,
|
||||
size: Size.square(18),
|
||||
),
|
||||
showTopBorder: false,
|
||||
showBottomBorder: false,
|
||||
onTap: () => onAction(
|
||||
MobileViewItemBottomSheetBodyAction.removeFromRecent,
|
||||
),
|
||||
);
|
||||
|
||||
case MobileViewItemBottomSheetBodyAction.divider:
|
||||
return const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: Divider(height: 0.5),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-7
@@ -89,7 +89,7 @@ class MobileViewBottomSheetBody extends StatelessWidget {
|
||||
children: [
|
||||
MobileQuickActionButton(
|
||||
text: LocaleKeys.button_rename.tr(),
|
||||
icon: FlowySvgs.m_rename_s,
|
||||
icon: FlowySvgs.view_item_rename_s,
|
||||
onTap: () => onAction(
|
||||
MobileViewBottomSheetBodyAction.rename,
|
||||
),
|
||||
@@ -99,10 +99,7 @@ class MobileViewBottomSheetBody extends StatelessWidget {
|
||||
text: isFavorite
|
||||
? LocaleKeys.button_removeFromFavorites.tr()
|
||||
: LocaleKeys.button_addToFavorites.tr(),
|
||||
icon: isFavorite
|
||||
? FlowySvgs.m_favorite_selected_lg
|
||||
: FlowySvgs.m_favorite_unselected_lg,
|
||||
iconColor: isFavorite ? Colors.yellow : null,
|
||||
icon: isFavorite ? FlowySvgs.unfavorite_s : FlowySvgs.favorite_s,
|
||||
onTap: () => onAction(
|
||||
isFavorite
|
||||
? MobileViewBottomSheetBodyAction.removeFromFavorites
|
||||
@@ -112,7 +109,7 @@ class MobileViewBottomSheetBody extends StatelessWidget {
|
||||
_divider(),
|
||||
MobileQuickActionButton(
|
||||
text: LocaleKeys.button_duplicate.tr(),
|
||||
icon: FlowySvgs.m_duplicate_s,
|
||||
icon: FlowySvgs.duplicate_s,
|
||||
onTap: () => onAction(
|
||||
MobileViewBottomSheetBodyAction.duplicate,
|
||||
),
|
||||
@@ -121,7 +118,7 @@ class MobileViewBottomSheetBody extends StatelessWidget {
|
||||
MobileQuickActionButton(
|
||||
text: LocaleKeys.button_delete.tr(),
|
||||
textColor: Theme.of(context).colorScheme.error,
|
||||
icon: FlowySvgs.m_delete_s,
|
||||
icon: FlowySvgs.trash_s,
|
||||
iconColor: Theme.of(context).colorScheme.error,
|
||||
onTap: () => onAction(
|
||||
MobileViewBottomSheetBodyAction.delete,
|
||||
|
||||
+124
-19
@@ -1,8 +1,15 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||
import 'package:appflowy/mobile/presentation/home/shared/mobile_page_card.dart';
|
||||
import 'package:appflowy/mobile/presentation/page_item/mobile_slide_action_button.dart';
|
||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/recent/recent_views_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
@@ -11,11 +18,14 @@ enum MobilePaneActionType {
|
||||
delete,
|
||||
addToFavorites,
|
||||
removeFromFavorites,
|
||||
more;
|
||||
more,
|
||||
add;
|
||||
|
||||
MobileSlideActionButton actionButton(
|
||||
BuildContext context,
|
||||
) {
|
||||
BuildContext context, {
|
||||
MobilePageCardType? cardType,
|
||||
FolderSpaceType? spaceType,
|
||||
}) {
|
||||
switch (this) {
|
||||
case MobilePaneActionType.delete:
|
||||
return MobileSlideActionButton(
|
||||
@@ -27,45 +37,89 @@ enum MobilePaneActionType {
|
||||
);
|
||||
case MobilePaneActionType.removeFromFavorites:
|
||||
return MobileSlideActionButton(
|
||||
backgroundColor: Colors.orange,
|
||||
svg: FlowySvgs.favorite_s,
|
||||
backgroundColor: const Color(0xFFFA217F),
|
||||
svg: FlowySvgs.favorite_section_remove_from_favorite_s,
|
||||
size: 24.0,
|
||||
onPressed: (context) => context
|
||||
.read<FavoriteBloc>()
|
||||
.add(FavoriteEvent.toggle(context.read<ViewBloc>().view)),
|
||||
);
|
||||
case MobilePaneActionType.addToFavorites:
|
||||
return MobileSlideActionButton(
|
||||
backgroundColor: Colors.orange,
|
||||
svg: FlowySvgs.m_favorite_unselected_lg,
|
||||
size: 34.0,
|
||||
backgroundColor: const Color(0xFF00C8FF),
|
||||
svg: FlowySvgs.favorite_s,
|
||||
size: 24.0,
|
||||
onPressed: (context) => context
|
||||
.read<FavoriteBloc>()
|
||||
.add(FavoriteEvent.toggle(context.read<ViewBloc>().view)),
|
||||
);
|
||||
case MobilePaneActionType.more:
|
||||
case MobilePaneActionType.add:
|
||||
return MobileSlideActionButton(
|
||||
backgroundColor: Colors.grey,
|
||||
svg: FlowySvgs.three_dots_vertical_s,
|
||||
backgroundColor: const Color(0xFF00C8FF),
|
||||
svg: FlowySvgs.add_m,
|
||||
size: 28.0,
|
||||
onPressed: (context) {
|
||||
final viewBloc = context.read<ViewBloc>();
|
||||
final view = viewBloc.state.view;
|
||||
final title = view.name;
|
||||
showMobileBottomSheet(
|
||||
context,
|
||||
showHeader: true,
|
||||
title: title,
|
||||
showDragHandle: true,
|
||||
showCloseButton: true,
|
||||
useRootNavigator: true,
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
builder: (sheetContext) {
|
||||
return AddNewPageWidgetBottomSheet(
|
||||
view: view,
|
||||
onAction: (layout) {
|
||||
Navigator.of(sheetContext).pop();
|
||||
viewBloc.add(
|
||||
ViewEvent.createView(
|
||||
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
layout,
|
||||
section: spaceType!.toViewSectionPB,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
case MobilePaneActionType.more:
|
||||
return MobileSlideActionButton(
|
||||
backgroundColor: const Color(0xE5515563),
|
||||
svg: FlowySvgs.three_dots_s,
|
||||
size: 24.0,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(10),
|
||||
bottomLeft: Radius.circular(10),
|
||||
),
|
||||
onPressed: (context) {
|
||||
final viewBloc = context.read<ViewBloc>();
|
||||
final favoriteBloc = context.read<FavoriteBloc>();
|
||||
final recentViewsBloc = context.read<RecentViewsBloc?>();
|
||||
showMobileBottomSheet(
|
||||
context,
|
||||
showDragHandle: true,
|
||||
showDivider: false,
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
useRootNavigator: true,
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
builder: (context) {
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider.value(value: viewBloc),
|
||||
BlocProvider.value(value: favoriteBloc),
|
||||
if (recentViewsBloc != null)
|
||||
BlocProvider.value(value: recentViewsBloc),
|
||||
],
|
||||
child: BlocBuilder<ViewBloc, ViewState>(
|
||||
builder: (context, state) {
|
||||
return MobileViewItemBottomSheet(
|
||||
view: viewBloc.state.view,
|
||||
actions: _buildActions(state.view, cardType: cardType),
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -76,19 +130,70 @@ enum MobilePaneActionType {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
List<MobileViewItemBottomSheetBodyAction> _buildActions(
|
||||
ViewPB view, {
|
||||
MobilePageCardType? cardType,
|
||||
}) {
|
||||
final isFavorite = view.isFavorite;
|
||||
|
||||
if (cardType != null) {
|
||||
switch (cardType) {
|
||||
case MobilePageCardType.recent:
|
||||
return [
|
||||
isFavorite
|
||||
? MobileViewItemBottomSheetBodyAction.removeFromFavorites
|
||||
: MobileViewItemBottomSheetBodyAction.addToFavorites,
|
||||
MobileViewItemBottomSheetBodyAction.divider,
|
||||
if (view.layout != ViewLayoutPB.Chat)
|
||||
MobileViewItemBottomSheetBodyAction.duplicate,
|
||||
MobileViewItemBottomSheetBodyAction.divider,
|
||||
MobileViewItemBottomSheetBodyAction.removeFromRecent,
|
||||
];
|
||||
case MobilePageCardType.favorite:
|
||||
return [
|
||||
isFavorite
|
||||
? MobileViewItemBottomSheetBodyAction.removeFromFavorites
|
||||
: MobileViewItemBottomSheetBodyAction.addToFavorites,
|
||||
MobileViewItemBottomSheetBodyAction.divider,
|
||||
MobileViewItemBottomSheetBodyAction.duplicate,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
isFavorite
|
||||
? MobileViewItemBottomSheetBodyAction.removeFromFavorites
|
||||
: MobileViewItemBottomSheetBodyAction.addToFavorites,
|
||||
MobileViewItemBottomSheetBodyAction.divider,
|
||||
MobileViewItemBottomSheetBodyAction.rename,
|
||||
if (view.layout != ViewLayoutPB.Chat)
|
||||
MobileViewItemBottomSheetBodyAction.duplicate,
|
||||
MobileViewItemBottomSheetBodyAction.divider,
|
||||
MobileViewItemBottomSheetBodyAction.delete,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
ActionPane buildEndActionPane(
|
||||
BuildContext context,
|
||||
List<MobilePaneActionType> actions,
|
||||
) {
|
||||
List<MobilePaneActionType> actions, {
|
||||
bool needSpace = true,
|
||||
MobilePageCardType? cardType,
|
||||
FolderSpaceType? spaceType,
|
||||
}) {
|
||||
return ActionPane(
|
||||
motion: const ScrollMotion(),
|
||||
extentRatio: actions.length / 5,
|
||||
children: actions
|
||||
.map(
|
||||
(action) => action.actionButton(context),
|
||||
)
|
||||
.toList(),
|
||||
children: [
|
||||
if (needSpace) const HSpace(20),
|
||||
...actions.map(
|
||||
(action) => action.actionButton(
|
||||
context,
|
||||
spaceType: spaceType,
|
||||
cardType: cardType,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_buttons.dart';
|
||||
import 'package:appflowy/plugins/base/drag_handler.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart' hide WidgetBuilder;
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
extension BottomSheetPaddingExtension on BuildContext {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import 'package:appflowy/mobile/presentation/base/mobile_view_page.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MobileChatScreen extends StatelessWidget {
|
||||
const MobileChatScreen({
|
||||
super.key,
|
||||
required this.id,
|
||||
this.title,
|
||||
});
|
||||
|
||||
/// view id
|
||||
final String id;
|
||||
final String? title;
|
||||
|
||||
static const routeName = '/chat';
|
||||
static const viewId = 'id';
|
||||
static const viewTitle = 'title';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MobileViewPage(
|
||||
id: id,
|
||||
title: title,
|
||||
viewLayout: ViewLayoutPB.Chat,
|
||||
);
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -15,6 +15,7 @@ import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
import 'package:appflowy_board/appflowy_board.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@@ -265,7 +266,7 @@ class _BoardContentState extends State<_BoardContent> {
|
||||
BoxDecoration _makeBoxDecoration(BuildContext context) {
|
||||
final themeMode = context.read<AppearanceSettingsCubit>().state.themeMode;
|
||||
return BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: AFThemeExtension.of(context).background,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
border: themeMode == ThemeMode.light
|
||||
? Border.fromBorderSide(
|
||||
|
||||
+5
-5
@@ -60,7 +60,7 @@ class _MobileBoardTrailingState extends State<MobileBoardTrailing> {
|
||||
child: IconButton(
|
||||
icon: Icon(
|
||||
Icons.close,
|
||||
color: style.colorScheme.onBackground,
|
||||
color: style.colorScheme.onSurface,
|
||||
),
|
||||
onPressed: () =>
|
||||
setState(() => _textController.clear()),
|
||||
@@ -86,7 +86,7 @@ class _MobileBoardTrailingState extends State<MobileBoardTrailing> {
|
||||
child: Text(
|
||||
LocaleKeys.button_cancel.tr(),
|
||||
style: style.textTheme.titleSmall?.copyWith(
|
||||
color: style.colorScheme.onBackground,
|
||||
color: style.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
onPressed: () => setState(() => isEditing = false),
|
||||
@@ -96,7 +96,7 @@ class _MobileBoardTrailingState extends State<MobileBoardTrailing> {
|
||||
LocaleKeys.button_add.tr(),
|
||||
style: style.textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: style.colorScheme.onBackground,
|
||||
color: style.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
@@ -117,14 +117,14 @@ class _MobileBoardTrailingState extends State<MobileBoardTrailing> {
|
||||
)
|
||||
: ElevatedButton.icon(
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor: style.colorScheme.onBackground,
|
||||
foregroundColor: style.colorScheme.onSurface,
|
||||
backgroundColor: style.colorScheme.secondary,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
).copyWith(
|
||||
overlayColor:
|
||||
MaterialStateProperty.all(Theme.of(context).hoverColor),
|
||||
WidgetStateProperty.all(Theme.of(context).hoverColor),
|
||||
),
|
||||
icon: const Icon(Icons.add),
|
||||
label: Text(
|
||||
|
||||
+2
-1
@@ -9,6 +9,7 @@ import 'package:appflowy/plugins/database/widgets/cell/card_cell_skeleton/text_c
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@@ -168,7 +169,7 @@ class MobileHiddenGroup extends StatelessWidget {
|
||||
return TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
textStyle: Theme.of(context).textTheme.bodyMedium,
|
||||
foregroundColor: Theme.of(context).colorScheme.onBackground,
|
||||
foregroundColor: AFThemeExtension.of(context).onBackground,
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
child: CardCellBuilder(
|
||||
|
||||
+4
-3
@@ -20,6 +20,7 @@ import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart'
|
||||
import 'package:appflowy/plugins/database/widgets/row/row_property.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/row_entities.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@@ -131,7 +132,7 @@ class _MobileRowDetailPageState extends State<MobileRowDetailPage> {
|
||||
void _showCardActions(BuildContext context) {
|
||||
showMobileBottomSheet(
|
||||
context,
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
backgroundColor: AFThemeExtension.of(context).background,
|
||||
showDragHandle: true,
|
||||
builder: (_) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -139,7 +140,7 @@ class _MobileRowDetailPageState extends State<MobileRowDetailPage> {
|
||||
MobileQuickActionButton(
|
||||
onTap: () =>
|
||||
_performAction(viewId, _bloc.state.currentRowId, false),
|
||||
icon: FlowySvgs.copy_s,
|
||||
icon: FlowySvgs.duplicate_s,
|
||||
text: LocaleKeys.button_duplicate.tr(),
|
||||
),
|
||||
const Divider(height: 8.5, thickness: 0.5),
|
||||
@@ -147,7 +148,7 @@ class _MobileRowDetailPageState extends State<MobileRowDetailPage> {
|
||||
onTap: () => _performAction(viewId, _bloc.state.currentRowId, true),
|
||||
text: LocaleKeys.button_delete.tr(),
|
||||
textColor: Theme.of(context).colorScheme.error,
|
||||
icon: FlowySvgs.m_delete_m,
|
||||
icon: FlowySvgs.trash_s,
|
||||
iconColor: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
const Divider(height: 8.5, thickness: 0.5),
|
||||
|
||||
+8
-4
@@ -2,6 +2,7 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/database/field/mobile_field_bottom_sheets.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -19,20 +20,23 @@ class MobileRowDetailCreateFieldButton extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: double.infinity),
|
||||
constraints: BoxConstraints(
|
||||
minWidth: double.infinity,
|
||||
minHeight: GridSize.headerHeight,
|
||||
),
|
||||
child: TextButton.icon(
|
||||
style: Theme.of(context).textButtonTheme.style?.copyWith(
|
||||
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
|
||||
shape: WidgetStateProperty.all<RoundedRectangleBorder>(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
),
|
||||
overlayColor: MaterialStateProperty.all<Color>(
|
||||
overlayColor: WidgetStateProperty.all<Color>(
|
||||
Theme.of(context).hoverColor,
|
||||
),
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
splashFactory: NoSplash.splashFactory,
|
||||
padding: const MaterialStatePropertyAll(
|
||||
padding: const WidgetStatePropertyAll(
|
||||
EdgeInsets.symmetric(vertical: 14, horizontal: 6),
|
||||
),
|
||||
),
|
||||
|
||||
+5
-2
@@ -1,3 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/type_option_menu_item.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||
@@ -7,7 +9,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.da
|
||||
import 'package:appflowy/util/field_type_extension.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import 'mobile_create_field_screen.dart';
|
||||
@@ -27,6 +29,7 @@ const mobileSupportedFieldTypes = [
|
||||
FieldType.CreatedTime,
|
||||
FieldType.Checkbox,
|
||||
FieldType.Checklist,
|
||||
// FieldType.Time,
|
||||
];
|
||||
|
||||
Future<FieldType?> showFieldTypeGridBottomSheet(
|
||||
@@ -40,7 +43,7 @@ Future<FieldType?> showFieldTypeGridBottomSheet(
|
||||
showCloseButton: true,
|
||||
elevation: 20,
|
||||
title: title,
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
backgroundColor: AFThemeExtension.of(context).background,
|
||||
enableDraggableScrollable: true,
|
||||
builder: (context) {
|
||||
final typeOptionMenuItemValue = mobileSupportedFieldTypes
|
||||
|
||||
+1
@@ -119,6 +119,7 @@ class FieldOptionValues {
|
||||
case FieldType.RichText:
|
||||
case FieldType.URL:
|
||||
case FieldType.Checkbox:
|
||||
case FieldType.Time:
|
||||
return null;
|
||||
case FieldType.Number:
|
||||
return NumberTypeOptionPB(
|
||||
|
||||
+1
-1
@@ -5,8 +5,8 @@ import 'package:appflowy/mobile/presentation/database/field/mobile_field_bottom_
|
||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_editor_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_backend_service.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_info.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_backend_service.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/setting/field_visibility_extension.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
+1
-1
@@ -438,7 +438,7 @@ class _SortDetailContent extends StatelessWidget {
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
splashFactory: NoSplash.splashFactory,
|
||||
overlayColor: const MaterialStatePropertyAll(
|
||||
overlayColor: const WidgetStatePropertyAll(
|
||||
Colors.transparent,
|
||||
),
|
||||
onTap: (index) {
|
||||
|
||||
+2
-1
@@ -11,6 +11,7 @@ import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@@ -183,7 +184,7 @@ class MobileDatabaseViewListButton extends StatelessWidget {
|
||||
showMobileBottomSheet(
|
||||
context,
|
||||
showDragHandle: true,
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
backgroundColor: AFThemeExtension.of(context).background,
|
||||
builder: (_) {
|
||||
return BlocProvider<ViewBloc>(
|
||||
create: (_) =>
|
||||
|
||||
+3
-3
@@ -107,9 +107,9 @@ enum _Action {
|
||||
|
||||
FlowySvgData get icon {
|
||||
return switch (this) {
|
||||
edit => FlowySvgs.edit_s,
|
||||
duplicate => FlowySvgs.copy_s,
|
||||
delete => FlowySvgs.delete_s,
|
||||
edit => FlowySvgs.view_item_rename_s,
|
||||
duplicate => FlowySvgs.duplicate_s,
|
||||
delete => FlowySvgs.trash_s,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
-1
@@ -234,7 +234,6 @@ class DatabaseViewSettingTile extends StatelessWidget {
|
||||
showHeader: true,
|
||||
showBackButton: true,
|
||||
title: LocaleKeys.grid_settings_properties.tr(),
|
||||
showDivider: true,
|
||||
builder: (_) {
|
||||
return BlocProvider.value(
|
||||
value: context.read<ViewBloc>(),
|
||||
|
||||
+6
-5
@@ -1,5 +1,3 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||
import 'package:appflowy/mobile/presentation/home/favorite_folder/mobile_home_favorite_folder.dart';
|
||||
@@ -10,6 +8,7 @@ import 'package:appflowy/workspace/application/user/user_workspace_bloc.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
|
||||
@@ -17,14 +16,15 @@ class MobileFavoritePageFolder extends StatelessWidget {
|
||||
const MobileFavoritePageFolder({
|
||||
super.key,
|
||||
required this.userProfile,
|
||||
required this.workspaceId,
|
||||
});
|
||||
|
||||
final UserProfilePB userProfile;
|
||||
final String workspaceId;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final workspaceId =
|
||||
context.read<UserWorkspaceBloc>().state.currentWorkspace?.workspaceId ??
|
||||
'';
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
@@ -67,7 +67,8 @@ class MobileFavoritePageFolder extends StatelessWidget {
|
||||
MobileFavoriteFolder(
|
||||
showHeader: false,
|
||||
forceExpanded: true,
|
||||
views: favoriteState.views,
|
||||
views:
|
||||
favoriteState.views.map((e) => e.item).toList(),
|
||||
),
|
||||
const VSpace(100.0),
|
||||
],
|
||||
|
||||
@@ -64,8 +64,6 @@ class MobileFavoriteScreen extends StatelessWidget {
|
||||
builder: (context, state) {
|
||||
return MobileFavoritePage(
|
||||
userProfile: userProfile,
|
||||
workspaceId: state.currentWorkspace?.workspaceId ??
|
||||
workspaceSetting.workspaceId,
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -81,11 +79,9 @@ class MobileFavoritePage extends StatelessWidget {
|
||||
const MobileFavoritePage({
|
||||
super.key,
|
||||
required this.userProfile,
|
||||
required this.workspaceId,
|
||||
});
|
||||
|
||||
final UserProfilePB userProfile;
|
||||
final String workspaceId;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -108,7 +104,6 @@ class MobileFavoritePage extends StatelessWidget {
|
||||
Expanded(
|
||||
child: MobileFavoritePageFolder(
|
||||
userProfile: userProfile,
|
||||
workspaceId: workspaceId,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||
import 'package:appflowy/mobile/presentation/home/shared/empty_placeholder.dart';
|
||||
import 'package:appflowy/mobile/presentation/home/shared/mobile_page_card.dart';
|
||||
import 'package:appflowy/util/theme_extension.dart';
|
||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/user/prelude.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/home_sizes.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class MobileFavoriteSpace extends StatefulWidget {
|
||||
const MobileFavoriteSpace({
|
||||
super.key,
|
||||
required this.userProfile,
|
||||
});
|
||||
|
||||
final UserProfilePB userProfile;
|
||||
|
||||
@override
|
||||
State<MobileFavoriteSpace> createState() => _MobileFavoriteSpaceState();
|
||||
}
|
||||
|
||||
class _MobileFavoriteSpaceState extends State<MobileFavoriteSpace>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
final workspaceId =
|
||||
context.read<UserWorkspaceBloc>().state.currentWorkspace?.workspaceId ??
|
||||
'';
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
create: (_) => SidebarSectionsBloc()
|
||||
..add(
|
||||
SidebarSectionsEvent.initial(widget.userProfile, workspaceId),
|
||||
),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (_) => FavoriteBloc()..add(const FavoriteEvent.initial()),
|
||||
),
|
||||
],
|
||||
child: BlocListener<UserWorkspaceBloc, UserWorkspaceState>(
|
||||
listener: (context, state) =>
|
||||
context.read<FavoriteBloc>().add(const FavoriteEvent.initial()),
|
||||
child: MultiBlocListener(
|
||||
listeners: [
|
||||
BlocListener<SidebarSectionsBloc, SidebarSectionsState>(
|
||||
listenWhen: (p, c) =>
|
||||
p.lastCreatedRootView?.id != c.lastCreatedRootView?.id,
|
||||
listener: (context, state) =>
|
||||
context.pushView(state.lastCreatedRootView!),
|
||||
),
|
||||
],
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final favoriteState = context.watch<FavoriteBloc>().state;
|
||||
|
||||
if (favoriteState.isLoading) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
if (favoriteState.views.isEmpty) {
|
||||
return const EmptySpacePlaceholder(
|
||||
type: MobilePageCardType.favorite,
|
||||
);
|
||||
}
|
||||
|
||||
return _FavoriteViews(
|
||||
favoriteViews: favoriteState.views.reversed.toList(),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _FavoriteViews extends StatelessWidget {
|
||||
const _FavoriteViews({
|
||||
required this.favoriteViews,
|
||||
});
|
||||
|
||||
final List<SectionViewPB> favoriteViews;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final borderColor = Theme.of(context).isLightMode
|
||||
? const Color(0xFFE9E9EC)
|
||||
: const Color(0x1AFFFFFF);
|
||||
return Scrollbar(
|
||||
child: ListView.separated(
|
||||
key: const PageStorageKey('favorite_views_page_storage_key'),
|
||||
padding: EdgeInsets.only(
|
||||
left: HomeSpaceViewSizes.mHorizontalPadding,
|
||||
right: HomeSpaceViewSizes.mHorizontalPadding,
|
||||
bottom: HomeSpaceViewSizes.mVerticalPadding +
|
||||
MediaQuery.of(context).padding.bottom,
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
final view = favoriteViews[index];
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 24.0),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: borderColor,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: MobileViewPage(
|
||||
key: ValueKey(view.item.id),
|
||||
view: view.item,
|
||||
timestamp: view.timestamp,
|
||||
type: MobilePageCardType.favorite,
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => const HSpace(8),
|
||||
itemCount: favoriteViews.length,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+14
-11
@@ -1,5 +1,3 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/default_mobile_action_pane.dart';
|
||||
import 'package:appflowy/mobile/presentation/home/favorite_folder/mobile_home_favorite_folder_header.dart';
|
||||
@@ -7,6 +5,7 @@ import 'package:appflowy/mobile/presentation/page_item/mobile_view_item.dart';
|
||||
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class MobileFavoriteFolder extends StatelessWidget {
|
||||
@@ -28,7 +27,7 @@ class MobileFavoriteFolder extends StatelessWidget {
|
||||
}
|
||||
|
||||
return BlocProvider<FolderBloc>(
|
||||
create: (context) => FolderBloc(type: FolderCategoryType.favorite)
|
||||
create: (context) => FolderBloc(type: FolderSpaceType.favorite)
|
||||
..add(
|
||||
const FolderEvent.initial(),
|
||||
),
|
||||
@@ -55,21 +54,25 @@ class MobileFavoriteFolder extends StatelessWidget {
|
||||
...views.map(
|
||||
(view) => MobileViewItem(
|
||||
key: ValueKey(
|
||||
'${FolderCategoryType.favorite.name} ${view.id}',
|
||||
'${FolderSpaceType.favorite.name} ${view.id}',
|
||||
),
|
||||
categoryType: FolderCategoryType.favorite,
|
||||
spaceType: FolderSpaceType.favorite,
|
||||
isDraggable: false,
|
||||
isFirstChild: view.id == views.first.id,
|
||||
isFeedback: false,
|
||||
view: view,
|
||||
level: 0,
|
||||
onSelected: context.pushView,
|
||||
endActionPane: (context) => buildEndActionPane(context, [
|
||||
view.isFavorite
|
||||
? MobilePaneActionType.removeFromFavorites
|
||||
: MobilePaneActionType.addToFavorites,
|
||||
MobilePaneActionType.more,
|
||||
]),
|
||||
endActionPane: (context) => buildEndActionPane(
|
||||
context,
|
||||
[
|
||||
view.isFavorite
|
||||
? MobilePaneActionType.removeFromFavorites
|
||||
: MobilePaneActionType.addToFavorites,
|
||||
MobilePaneActionType.more,
|
||||
],
|
||||
spaceType: FolderSpaceType.favorite,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import 'package:appflowy/mobile/presentation/home/mobile_folders.dart';
|
||||
import 'package:appflowy/workspace/application/user/user_workspace_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/home_sizes.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class MobileHomeSpace extends StatefulWidget {
|
||||
const MobileHomeSpace({super.key, required this.userProfile});
|
||||
|
||||
final UserProfilePB userProfile;
|
||||
|
||||
@override
|
||||
State<MobileHomeSpace> createState() => _MobileHomeSpaceState();
|
||||
}
|
||||
|
||||
class _MobileHomeSpaceState extends State<MobileHomeSpace>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
final workspaceId =
|
||||
context.read<UserWorkspaceBloc>().state.currentWorkspace?.workspaceId ??
|
||||
'';
|
||||
return Scrollbar(
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: HomeSpaceViewSizes.mHorizontalPadding,
|
||||
right: HomeSpaceViewSizes.mHorizontalPadding,
|
||||
top: HomeSpaceViewSizes.mVerticalPadding,
|
||||
bottom: HomeSpaceViewSizes.mVerticalPadding +
|
||||
MediaQuery.of(context).padding.bottom,
|
||||
),
|
||||
child: MobileFolders(
|
||||
user: widget.userProfile,
|
||||
workspaceId: workspaceId,
|
||||
showFavorite: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||
import 'package:appflowy/mobile/presentation/home/home.dart';
|
||||
import 'package:appflowy/mobile/presentation/home/section_folder/mobile_home_section_folder.dart';
|
||||
import 'package:appflowy/mobile/presentation/home/space/mobile_space.dart';
|
||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/user/user_workspace_bloc.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
@@ -11,6 +15,7 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
// Contains Public And Private Sections
|
||||
class MobileFolders extends StatelessWidget {
|
||||
@@ -31,16 +36,15 @@ class MobileFolders extends StatelessWidget {
|
||||
providers: [
|
||||
BlocProvider(
|
||||
create: (_) => SidebarSectionsBloc()
|
||||
..add(
|
||||
SidebarSectionsEvent.initial(
|
||||
user,
|
||||
workspaceId,
|
||||
),
|
||||
),
|
||||
..add(SidebarSectionsEvent.initial(user, workspaceId)),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (_) => FavoriteBloc()..add(const FavoriteEvent.initial()),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (_) => SpaceBloc()
|
||||
..add(SpaceEvent.initial(user, workspaceId, openFirstPage: false)),
|
||||
),
|
||||
],
|
||||
child: BlocListener<UserWorkspaceBloc, UserWorkspaceState>(
|
||||
listener: (context, state) {
|
||||
@@ -50,50 +54,111 @@ class MobileFolders extends StatelessWidget {
|
||||
state.currentWorkspace?.workspaceId ?? workspaceId,
|
||||
),
|
||||
);
|
||||
context.read<SpaceBloc>().add(
|
||||
SpaceEvent.reset(
|
||||
user,
|
||||
state.currentWorkspace?.workspaceId ?? workspaceId,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: BlocConsumer<SidebarSectionsBloc, SidebarSectionsState>(
|
||||
listenWhen: (p, c) =>
|
||||
p.lastCreatedRootView?.id != c.lastCreatedRootView?.id,
|
||||
listener: (context, state) {
|
||||
final lastCreatedRootView = state.lastCreatedRootView;
|
||||
if (lastCreatedRootView != null) {
|
||||
context.pushView(lastCreatedRootView);
|
||||
}
|
||||
},
|
||||
builder: (context, state) {
|
||||
final isCollaborativeWorkspace =
|
||||
context.read<UserWorkspaceBloc>().state.isCollabWorkspaceOn;
|
||||
return SlidableAutoCloseBehavior(
|
||||
child: Column(
|
||||
children: [
|
||||
...isCollaborativeWorkspace
|
||||
? [
|
||||
MobileSectionFolder(
|
||||
title: LocaleKeys.sideBar_workspace.tr(),
|
||||
categoryType: FolderCategoryType.public,
|
||||
views: state.section.publicViews,
|
||||
),
|
||||
const VSpace(8.0),
|
||||
MobileSectionFolder(
|
||||
title: LocaleKeys.sideBar_private.tr(),
|
||||
categoryType: FolderCategoryType.private,
|
||||
views: state.section.privateViews,
|
||||
),
|
||||
]
|
||||
: [
|
||||
MobileSectionFolder(
|
||||
title: LocaleKeys.sideBar_personal.tr(),
|
||||
categoryType: FolderCategoryType.public,
|
||||
views: state.section.publicViews,
|
||||
),
|
||||
],
|
||||
const VSpace(8.0),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
child: MultiBlocListener(
|
||||
listeners: [
|
||||
BlocListener<SpaceBloc, SpaceState>(
|
||||
listenWhen: (p, c) =>
|
||||
p.lastCreatedPage?.id != c.lastCreatedPage?.id,
|
||||
listener: (context, state) {
|
||||
final lastCreatedPage = state.lastCreatedPage;
|
||||
if (lastCreatedPage != null) {
|
||||
context.pushView(lastCreatedPage);
|
||||
}
|
||||
},
|
||||
),
|
||||
BlocListener<SidebarSectionsBloc, SidebarSectionsState>(
|
||||
listenWhen: (p, c) =>
|
||||
p.lastCreatedRootView?.id != c.lastCreatedRootView?.id,
|
||||
listener: (context, state) {
|
||||
final lastCreatedPage = state.lastCreatedRootView;
|
||||
if (lastCreatedPage != null) {
|
||||
context.pushView(lastCreatedPage);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
child: BlocBuilder<SidebarSectionsBloc, SidebarSectionsState>(
|
||||
builder: (context, state) {
|
||||
return SlidableAutoCloseBehavior(
|
||||
child: Column(
|
||||
children: [
|
||||
..._buildSpaceOrSection(context, state),
|
||||
const VSpace(4.0),
|
||||
const _TrashButton(),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildSpaceOrSection(
|
||||
BuildContext context,
|
||||
SidebarSectionsState state,
|
||||
) {
|
||||
if (context.watch<SpaceBloc>().state.spaces.isNotEmpty) {
|
||||
return [
|
||||
const MobileSpace(),
|
||||
];
|
||||
}
|
||||
|
||||
if (context.read<UserWorkspaceBloc>().state.isCollabWorkspaceOn) {
|
||||
return [
|
||||
MobileSectionFolder(
|
||||
title: LocaleKeys.sideBar_workspace.tr(),
|
||||
spaceType: FolderSpaceType.public,
|
||||
views: state.section.publicViews,
|
||||
),
|
||||
const VSpace(8.0),
|
||||
MobileSectionFolder(
|
||||
title: LocaleKeys.sideBar_private.tr(),
|
||||
spaceType: FolderSpaceType.private,
|
||||
views: state.section.privateViews,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
MobileSectionFolder(
|
||||
title: LocaleKeys.sideBar_personal.tr(),
|
||||
spaceType: FolderSpaceType.public,
|
||||
views: state.section.publicViews,
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class _TrashButton extends StatelessWidget {
|
||||
const _TrashButton();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 52,
|
||||
child: FlowyButton(
|
||||
expand: true,
|
||||
margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 2.0),
|
||||
leftIcon: const FlowySvg(
|
||||
FlowySvgs.m_delete_s,
|
||||
),
|
||||
leftIconSize: const Size.square(18),
|
||||
iconPadding: 10.0,
|
||||
text: FlowyText.regular(
|
||||
LocaleKeys.trash_text.tr(),
|
||||
fontSize: 16.0,
|
||||
),
|
||||
onTap: () => context.push(MobileHomeTrashPage.routeName),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loaded 100 of 1597 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user