Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59dff54edb | ||
|
|
d358e18f33 | ||
|
|
dd9b1fb78f | ||
|
|
e08a1a6974 | ||
|
|
7f4e7e6aa0 | ||
|
|
18bd91936c | ||
|
|
b9a25f449f | ||
|
|
993532a2f0 | ||
|
|
8a735799c6 | ||
|
|
197da5f425 | ||
|
|
e28e5a0649 | ||
|
|
09b4e19c9d | ||
|
|
653c831473 | ||
|
|
a2f2cdebb7 | ||
|
|
59e0a1325e | ||
|
|
c0446d7a96 | ||
|
|
1fbee02731 | ||
|
|
102fdbce5c | ||
|
|
71c6beb882 | ||
|
|
ce83042317 | ||
|
|
03598d3f22 | ||
|
|
c2eb2014a3 | ||
|
|
9416ba1bfc | ||
|
|
8c3984d21a | ||
|
|
1883dd6d7c | ||
|
|
a354f9fe52 | ||
|
|
41e2c74b5e | ||
|
|
74d9d427bd | ||
|
|
aa27c4e6d4 | ||
|
|
f40ae9ff25 | ||
|
|
e66087861b | ||
|
|
147637fcf5 | ||
|
|
48582cb718 | ||
|
|
ad21a61ffb | ||
|
|
d34eec1d99 | ||
|
|
95aeac201f | ||
|
|
cceee80799 | ||
|
|
71f80be8f7 | ||
|
|
16a4babdca | ||
|
|
85c0622245 | ||
|
|
e3410ec568 | ||
|
|
c0b0f12b0d | ||
|
|
70a2b7d9ab | ||
|
|
a84c47dc33 | ||
|
|
56c5d69b0f | ||
|
|
6c3d7d2079 | ||
|
|
25a98cda81 | ||
|
|
cd6e133c86 | ||
|
|
b16a102f85 | ||
|
|
d51c7f382f | ||
|
|
a57ca5c0cb | ||
|
|
1e9137fcfe | ||
|
|
8e5b6b6e27 | ||
|
|
7e101b8bf1 | ||
|
|
bf0cfa798c | ||
|
|
1793a28cbc | ||
|
|
e565d0ee32 | ||
|
|
f60462a853 | ||
|
|
966547faa0 | ||
|
|
3647af44c9 | ||
|
|
25b4a647b0 | ||
|
|
c6e3c1fd7c | ||
|
|
4fd35d0de4 | ||
|
|
7406c5e6a5 | ||
|
|
aa70885b21 | ||
|
|
8699c9c125 | ||
|
|
bc758e0b06 | ||
|
|
e38980ccc3 | ||
|
|
8dd180046f | ||
|
|
0cd2726752 | ||
|
|
11daa15b89 | ||
|
|
8436bc6144 | ||
|
|
59851f46b7 | ||
|
|
62f1d6eb82 | ||
|
|
ec80f72c8f | ||
|
|
b2c80981cc | ||
|
|
8fa425a76c | ||
|
|
ebe112581d | ||
|
|
bc8f35d7db | ||
|
|
058eeec932 | ||
|
|
8e10cba8e5 | ||
|
|
7a24f6b6f0 | ||
|
|
4f40945458 | ||
|
|
e59870e421 | ||
|
|
cf521fe20b | ||
|
|
f0d47ee50c | ||
|
|
2a5d393c9f | ||
|
|
98e0b832ec | ||
|
|
77e7cbe9ec | ||
|
|
acd53eb640 |
No files matched your search
@@ -22,15 +22,297 @@ on:
|
||||
- "!frontend/appflowy_tauri/**"
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.10.1"
|
||||
CARGO_TERM_COLOR: always
|
||||
FLUTTER_VERSION: "3.10.4"
|
||||
RUST_TOOLCHAIN: "1.70"
|
||||
CARGO_MAKE_VERSION: "0.36.6"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
if: github.event.pull_request.draft != true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
flutter_profile: development-linux-x86_64
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
flutter_profile: development-mac-x86_64
|
||||
target: x86_64-apple-darwin
|
||||
- os: windows-latest
|
||||
flutter_profile: development-windows-x86
|
||||
target: x86_64-pc-windows-msvc
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
# the following step is required to avoid running out of space
|
||||
- name: Maximize build space
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo rm -rf /usr/share/dotnet
|
||||
sudo rm -rf /opt/ghc
|
||||
sudo rm -rf "/usr/local/share/boost"
|
||||
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
|
||||
|
||||
- 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 }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- name: Install flutter
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: ${{ matrix.os }}
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
cache-all-crates: true
|
||||
|
||||
- uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-make@${{ env.CARGO_MAKE_VERSION }}, duckscript_cli
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
vcpkg integrate install
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
echo 'do nothing'
|
||||
fi
|
||||
cargo make appflowy-flutter-deps-tools
|
||||
shell: bash
|
||||
|
||||
- name: Build AppFlowy
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make --profile ${{ matrix.flutter_profile }} appflowy-core-dev
|
||||
|
||||
- name: Run code generation
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make code_generation
|
||||
|
||||
- name: Flutter Analyzer
|
||||
working-directory: frontend/appflowy_flutter
|
||||
run: |
|
||||
flutter analyze .
|
||||
|
||||
- name: Compress appflowy_flutter
|
||||
run: tar -czf appflowy_flutter.tar.gz frontend/appflowy_flutter
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ github.run_id }}-${{ matrix.os }}
|
||||
path: appflowy_flutter.tar.gz
|
||||
|
||||
unit_test:
|
||||
needs: [prepare]
|
||||
if: github.event.pull_request.draft != true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest]
|
||||
include:
|
||||
- os: macos-latest
|
||||
flutter_profile: development-mac-x86_64
|
||||
target: x86_64-apple-darwin
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
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 }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- name: Install flutter
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: ${{ matrix.os }}
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
cache-all-crates: true
|
||||
|
||||
- uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-make@${{ env.CARGO_MAKE_VERSION }}, duckscript_cli
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "macOS" ]; then
|
||||
cargo make appflowy-flutter-deps-tools
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Run Flutter unit tests
|
||||
working-directory: frontend
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "macOS" ]; then
|
||||
flutter config --enable-macos-desktop
|
||||
cargo make --profile ${{ matrix.flutter_profile }} appflowy-dev
|
||||
cargo make dart_unit_test
|
||||
else
|
||||
cargo make dart_unit_test_no_build
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: Wandalen/wretry.action@v1.0.36
|
||||
with:
|
||||
action: codecov/codecov-action@v3
|
||||
with: |
|
||||
name: appflowy
|
||||
flags: appflowy_flutter_unit_test
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
os: ${{ matrix.os }}
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
attempt_limit: 20
|
||||
attempt_delay: 10000
|
||||
|
||||
integration_test:
|
||||
needs: [prepare]
|
||||
if: github.event.pull_request.draft != true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest,windows-latest]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
flutter_profile: development-linux-x86_64
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: windows-latest
|
||||
flutter_profile: development-windows-x86
|
||||
target: x86_64-pc-windows-msvc
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
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 }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- name: Install flutter
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
|
||||
- uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-make@${{ env.CARGO_MAKE_VERSION }}
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Enable Flutter Desktop
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
flutter config --enable-linux-desktop
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
flutter config --enable-macos-desktop
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
git config --system core.longpaths true
|
||||
flutter config --enable-windows-desktop
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ${{ github.run_id }}-${{ matrix.os }}
|
||||
|
||||
- name: Uncompress appflowy_flutter
|
||||
run: tar -xf appflowy_flutter.tar.gz
|
||||
|
||||
- name: Run code generation
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make code_generation
|
||||
|
||||
- name: Run Flutter integration tests
|
||||
working-directory: frontend/appflowy_flutter
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
export DISPLAY=:99
|
||||
sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 &
|
||||
sudo apt-get install network-manager
|
||||
flutter test integration_test/runner.dart -d Linux --coverage
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
flutter test integration_test/runner.dart -d macOS --coverage
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
flutter test integration_test/runner.dart -d Windows --coverage
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: Wandalen/wretry.action@v1.0.36
|
||||
with:
|
||||
action: codecov/codecov-action@v3
|
||||
with: |
|
||||
name: appflowy
|
||||
flags: appflowy_flutter_integrateion_test
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
os: ${{ matrix.os }}
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
attempt_limit: 20
|
||||
attempt_delay: 10000
|
||||
|
||||
build:
|
||||
needs: [prepare]
|
||||
if: github.event.pull_request.draft != true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -67,34 +349,20 @@ jobs:
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- uses: taiki-e/install-action@v2
|
||||
with:
|
||||
prefix-key: ${{ matrix.os }}
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
|
||||
- uses: davidB/rust-cargo-make@v1
|
||||
with:
|
||||
version: '0.36.6'
|
||||
tool: cargo-make@${{ env.CARGO_MAKE_VERSION }}
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo install --force duckscript_cli
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
|
||||
sudo apt-get install keybinder-3.0 libnotify-dev
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
vcpkg integrate install
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
echo 'do nothing'
|
||||
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
|
||||
fi
|
||||
cargo make appflowy-flutter-deps-tools
|
||||
shell: bash
|
||||
|
||||
- name: Enable Flutter Desktop
|
||||
@@ -109,32 +377,20 @@ jobs:
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Build AppFlowy
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make --profile ${{ matrix.flutter_profile }} appflowy-dev
|
||||
|
||||
- name: Flutter Analyzer
|
||||
working-directory: frontend/appflowy_flutter
|
||||
run: |
|
||||
flutter analyze .
|
||||
|
||||
- name: Run Flutter unit tests
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make dart_unit_test
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: Wandalen/wretry.action@v1.0.36
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
action: codecov/codecov-action@v3
|
||||
with: |
|
||||
name: appflowy
|
||||
flags: appflowy_flutter_unit_test
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
os: ${{ matrix.os }}
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
attempt_limit: 20
|
||||
attempt_delay: 10000
|
||||
name: ${{ github.run_id }}-${{ matrix.os }}
|
||||
|
||||
- name: Uncompress appflowy_flutter
|
||||
run: tar -xf appflowy_flutter.tar.gz
|
||||
|
||||
- name: Run code generation
|
||||
if: matrix.os == 'windows-latest'
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make code_generation
|
||||
|
||||
- name: Build flutter product
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make --profile ${{ matrix.flutter_profile }} appflowy-make-product-dev
|
||||
@@ -1,26 +1,23 @@
|
||||
name: integration test
|
||||
name: Mobile-CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- ".github/workflows/integration_test.yml"
|
||||
- ".github/workflows/mobile_ci.yaml"
|
||||
- "frontend/**"
|
||||
- "!frontend/appflowy_tauri/**"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- ".github/workflows/integration_test.yml"
|
||||
- ".github/workflows/mobile_ci.yaml"
|
||||
- "frontend/**"
|
||||
- "!frontend/appflowy_tauri/**"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
FLUTTER_VERSION: "3.10.1"
|
||||
RUST_TOOLCHAIN: "1.70"
|
||||
|
||||
@@ -34,17 +31,22 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
os: [ubuntu-latest]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
flutter_profile: development-linux-x86_64
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: windows-latest
|
||||
flutter_profile: development-windows-x86
|
||||
target: x86_64-pc-windows-msvc
|
||||
target: aarch64-linux-android
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
# the following step is required to avoid running out of space
|
||||
- name: Maximize build space
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo rm -rf /usr/share/dotnet
|
||||
sudo rm -rf /opt/ghc
|
||||
sudo rm -rf "/usr/local/share/boost"
|
||||
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
|
||||
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -65,27 +67,36 @@ jobs:
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
- uses: nttld/setup-ndk@v1
|
||||
id: setup-ndk
|
||||
with:
|
||||
ndk-version: "r24"
|
||||
add-to-path: true
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: ${{ matrix.os }}
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
cache-all-crates: true
|
||||
|
||||
- uses: davidB/rust-cargo-make@v1
|
||||
with:
|
||||
version: '0.36.6'
|
||||
version: "0.36.6"
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
rustup target install aarch64-linux-android
|
||||
rustup target install x86_64-linux-android
|
||||
cargo install --force duckscript_cli
|
||||
cargo install cargo-ndk
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
|
||||
sudo apt-get install keybinder-3.0 libnotify-dev
|
||||
sudo apt-get install gcc-multilib
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
vcpkg integrate install
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
@@ -94,48 +105,9 @@ jobs:
|
||||
cargo make appflowy-flutter-deps-tools
|
||||
shell: bash
|
||||
|
||||
- name: Enable Flutter Desktop
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
flutter config --enable-linux-desktop
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
flutter config --enable-macos-desktop
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
git config --system core.longpaths true
|
||||
flutter config --enable-windows-desktop
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Build AppFlowy
|
||||
working-directory: frontend
|
||||
env:
|
||||
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
run: |
|
||||
cargo make --profile ${{ matrix.flutter_profile }} appflowy-dev
|
||||
|
||||
- name: Run Flutter integration tests
|
||||
working-directory: frontend/appflowy_flutter
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
export DISPLAY=:99
|
||||
sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 &
|
||||
sudo apt-get install network-manager
|
||||
flutter test integration_test/runner.dart -d Linux --coverage
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
flutter test integration_test/runner.dart -d macOS --coverage
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
flutter test integration_test/runner.dart -d Windows --coverage
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: Wandalen/wretry.action@v1.0.36
|
||||
with:
|
||||
action: codecov/codecov-action@v3
|
||||
with: |
|
||||
name: appflowy
|
||||
flags: appflowy_flutter_integrateion_test
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
os: ${{ matrix.os }}
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
attempt_limit: 20
|
||||
attempt_delay: 10000
|
||||
cargo make --profile development-android appflowy-android-dev
|
||||
@@ -60,8 +60,8 @@ jobs:
|
||||
echo SUPABASE_ANON_KEY=${{ secrets.SUPABASE_ANON_KEY }} >> .env.ci
|
||||
echo SUPABASE_JWT_SECRET=${{ secrets.SUPABASE_JWT_SECRET }} >> .env.ci
|
||||
|
||||
- name: Create .env file in flowy-test
|
||||
working-directory: frontend/rust-lib/flowy-test
|
||||
- name: Create .env file in event-integration
|
||||
working-directory: frontend/rust-lib/event-integration
|
||||
run: |
|
||||
touch .env.ci
|
||||
echo SUPABASE_URL=${{ secrets.SUPABASE_URL }} >> .env.ci
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="ProtoBuf_Gen" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="run --manifest-path ${flowy_tool} -- pb-gen --rust_sources=${rust_lib},${shared_lib} --derive_meta=${derive_meta} --flutter_package_lib=${flutter_package_lib}" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$/frontend" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="emulateTerminal" value="false" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<envs>
|
||||
<env name="flowy_tool" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/scripts/flowy-tool/Cargo.toml" />
|
||||
<env name="rust_lib" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/" />
|
||||
<env name="shared_lib" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/../shared_lib" />
|
||||
<env name="flutter_lib" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/appflowy_flutter/packages" />
|
||||
<env name="derive_meta" value="${shared_lib}/flowy-derive/src/derive_cache/derive_cache.rs" />
|
||||
<env name="flutter_package_lib" value="${flutter_lib}/flowy_sdk/lib" />
|
||||
</envs>
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,18 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run backend" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="run --package backend --bin backend" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$/backend" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="emulateTerminal" value="false" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<envs>
|
||||
<env name="APP_ENVIRONMENT" value="production" />
|
||||
</envs>
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,16 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="dart-event" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="run --manifest-path $PROJECT_DIR$/scripts/flowy-tool/Cargo.toml -- dart-event --rust_source=$PROJECT_DIR$/rust-lib/ --output=$PROJECT_DIR$/appflowy_flutter/packages/flowy_sdk/lib/dispatch/dart_event.dart" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="emulateTerminal" value="false" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<envs />
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,5 +1,38 @@
|
||||
# Release Notes
|
||||
|
||||
## Version 0.3.7 - 10/30/2023
|
||||
|
||||
### New Features
|
||||
- Support showing checklist items inline in row page.
|
||||
- Support inserting date from slash menu.
|
||||
- Support renaming a stack directly by clicking on the stack name.
|
||||
- Show the detailed reminder content in the notification center.
|
||||
- Save card order in Board view.
|
||||
- Allow to hide the ungrouped stack.
|
||||
- Segmented the checklist progress bar.
|
||||
|
||||
### Bug fixes
|
||||
- Optimize side panel animation.
|
||||
- Fix calendar with hidden date or title doesn't show options correctly.
|
||||
- Fix the horizontal scroll bar disappears in Grid view.
|
||||
- Improve setting tab UI in Grid view.
|
||||
- Improve theme of the code block.
|
||||
- Fix some UI issues.
|
||||
|
||||
## Version 0.3.6 - 10/16/2023
|
||||
|
||||
### New Features
|
||||
- Support setting Markdown styles through keyboard shortcuts.
|
||||
- Added Ukrainian language.
|
||||
- Support auto-hiding sidebar feature, ensuring a streamlined view even when resizing to a smaller window.
|
||||
- Support toggling the notifitcation on/off.
|
||||
- Added Lemonade theme.
|
||||
|
||||
### Bug fixes
|
||||
- Improve Vietnamese translations.
|
||||
- Improve reminder feature.
|
||||
- Fix some UI issues.
|
||||
|
||||
## Version 0.3.5 - 10/09/2023
|
||||
|
||||
### New Features
|
||||
|
||||
@@ -18,7 +18,7 @@ You are in charge of your data and customizations.
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="http://www.appflowy.io"><b>Website</b></a> •
|
||||
<a href="https://www.appflowy.io"><b>Website</b></a> •
|
||||
<a href="https://discord.gg/9Q2xaN37tV"><b>Discord</b></a> •
|
||||
<a href="https://twitter.com/appflowy"><b>Twitter</b></a>
|
||||
</p>
|
||||
|
||||
@@ -4,6 +4,18 @@
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
// This task only builds the Dart code of AppFlowy.
|
||||
// It supports both the desktop and mobile version.
|
||||
"name": "AF: Build Dart Only",
|
||||
"request": "launch",
|
||||
"program": "./lib/main.dart",
|
||||
"type": "dart",
|
||||
"env": {
|
||||
"RUST_LOG": "debug",
|
||||
},
|
||||
"cwd": "${workspaceRoot}/appflowy_flutter"
|
||||
},
|
||||
{
|
||||
// This task builds the Rust and Dart code of AppFlowy.
|
||||
"name": "AF-desktop: Build All",
|
||||
@@ -17,17 +29,6 @@
|
||||
},
|
||||
"cwd": "${workspaceRoot}/appflowy_flutter"
|
||||
},
|
||||
{
|
||||
// This task only builds the Dart code of AppFlowy.
|
||||
"name": "AF-desktop: Build Dart Only",
|
||||
"request": "launch",
|
||||
"program": "./lib/main.dart",
|
||||
"type": "dart",
|
||||
"env": {
|
||||
"RUST_LOG": "debug",
|
||||
},
|
||||
"cwd": "${workspaceRoot}/appflowy_flutter"
|
||||
},
|
||||
{
|
||||
// This task builds will:
|
||||
// - call the clean task,
|
||||
@@ -65,6 +66,17 @@
|
||||
},
|
||||
"cwd": "${workspaceRoot}/appflowy_flutter"
|
||||
},
|
||||
{
|
||||
"name": "AF-Android: Clean + Rebuild All",
|
||||
"request": "launch",
|
||||
"program": "./lib/main.dart",
|
||||
"type": "dart",
|
||||
"preLaunchTask": "AF: Clean + Rebuild All (Android)",
|
||||
"env": {
|
||||
"RUST_LOG": "trace"
|
||||
},
|
||||
"cwd": "${workspaceRoot}/appflowy_flutter"
|
||||
},
|
||||
{
|
||||
"name": "AF-desktop: Debug Rust",
|
||||
"request": "attach",
|
||||
|
||||
@@ -84,6 +84,34 @@
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Clean + Rebuild All (Android)",
|
||||
"type": "shell",
|
||||
"dependsOrder": "sequence",
|
||||
"dependsOn": [
|
||||
"AF: Dart Clean",
|
||||
"AF: Flutter Clean",
|
||||
"AF: Build Appflowy Core For Android",
|
||||
"AF: Flutter Pub Get",
|
||||
"AF: Flutter Package Get",
|
||||
"AF: Generate Language Files",
|
||||
"AF: Generate Freezed Files",
|
||||
"AF: Generate Svg Files"
|
||||
],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Build Appflowy Core For Android",
|
||||
"type": "shell",
|
||||
"command": "cargo make --profile development-android appflowy-core-dev-android",
|
||||
"group": "build",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Build Appflowy Core",
|
||||
"type": "shell",
|
||||
|
||||
@@ -25,7 +25,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
|
||||
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
|
||||
CARGO_MAKE_CRATE_NAME = "dart-ffi"
|
||||
LIB_NAME = "dart_ffi"
|
||||
CURRENT_APP_VERSION = "0.3.5"
|
||||
CURRENT_APP_VERSION = "0.3.7"
|
||||
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite"
|
||||
PRODUCT_NAME = "AppFlowy"
|
||||
# CRATE_TYPE: https://doc.rust-lang.org/reference/linkage.html
|
||||
@@ -186,6 +186,29 @@ RUST_COMPILE_TARGET = "aarch64-apple-ios"
|
||||
BUILD_ARCHS = "arm64"
|
||||
CRATE_TYPE = "staticlib"
|
||||
|
||||
[env.production-ios-arm64]
|
||||
BUILD_FLAG = "release"
|
||||
TARGET_OS = "ios"
|
||||
FLUTTER_OUTPUT_DIR = "Release"
|
||||
RUST_COMPILE_TARGET = "aarch64-apple-ios"
|
||||
BUILD_ARCHS = "arm64"
|
||||
CRATE_TYPE = "staticlib"
|
||||
|
||||
[env.development-android]
|
||||
BUILD_FLAG = "debug"
|
||||
TARGET_OS = "android"
|
||||
CRATE_TYPE = "cdylib"
|
||||
FLUTTER_OUTPUT_DIR = "Debug"
|
||||
LIB_EXT = "so"
|
||||
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite,openssl_vendored"
|
||||
|
||||
[env.production-android]
|
||||
BUILD_FLAG = "release"
|
||||
TARGET_OS = "android"
|
||||
CRATE_TYPE = "cdylib"
|
||||
FLUTTER_OUTPUT_DIR = "Release"
|
||||
LIB_EXT = "so"
|
||||
|
||||
[tasks.echo_env]
|
||||
script = ['''
|
||||
echo "-------- Env Parameters --------"
|
||||
@@ -201,21 +224,6 @@ script = ['''
|
||||
''']
|
||||
script_runner = "@shell"
|
||||
|
||||
[env.development-android]
|
||||
BUILD_FLAG = "debug"
|
||||
TARGET_OS = "android"
|
||||
CRATE_TYPE = "cdylib"
|
||||
FLUTTER_OUTPUT_DIR = "Debug"
|
||||
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite,openssl_vendored"
|
||||
|
||||
[env.production-android]
|
||||
CARGO_PROFILE = "release"
|
||||
BUILD_FLAG = "release"
|
||||
TARGET_OS = "android"
|
||||
CRATE_TYPE = "cdylib"
|
||||
FLUTTER_OUTPUT_DIR = "Release"
|
||||
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite,openssl_vendored"
|
||||
|
||||
[tasks.setup-crate-type]
|
||||
private = true
|
||||
script = [
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
# This file should be version controlled.
|
||||
|
||||
version:
|
||||
revision: 135454af32477f815a7525073027a3ff9eff1bfd
|
||||
channel: stable
|
||||
revision: 682aa387cfe4fbd71ccd5418b2c2a075729a1c66
|
||||
channel: unknown
|
||||
|
||||
project_type: app
|
||||
|
||||
@@ -13,11 +13,11 @@ project_type: app
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: 135454af32477f815a7525073027a3ff9eff1bfd
|
||||
base_revision: 135454af32477f815a7525073027a3ff9eff1bfd
|
||||
- platform: windows
|
||||
create_revision: 135454af32477f815a7525073027a3ff9eff1bfd
|
||||
base_revision: 135454af32477f815a7525073027a3ff9eff1bfd
|
||||
create_revision: 682aa387cfe4fbd71ccd5418b2c2a075729a1c66
|
||||
base_revision: 682aa387cfe4fbd71ccd5418b2c2a075729a1c66
|
||||
- platform: android
|
||||
create_revision: 682aa387cfe4fbd71ccd5418b2c2a075729a1c66
|
||||
base_revision: 682aa387cfe4fbd71ccd5418b2c2a075729a1c66
|
||||
|
||||
# User provided section
|
||||
|
||||
|
||||
@@ -11,3 +11,5 @@ GeneratedPluginRegistrant.java
|
||||
key.properties
|
||||
**/*.keystore
|
||||
**/*.jks
|
||||
|
||||
.cxx
|
||||
@@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion 31
|
||||
compileSdkVersion 33
|
||||
ndkVersion "24.0.8215888"
|
||||
|
||||
compileOptions {
|
||||
@@ -46,11 +46,16 @@ android {
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "io.appflowy.appflowy"
|
||||
minSdkVersion 19
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 31
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
multiDexEnabled true
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_STL=c++_shared"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@@ -63,6 +68,14 @@ android {
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
}
|
||||
|
||||
namespace 'io.appflowy.appflowy'
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path "src/main/CMakeLists.txt"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flutter {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="io.appflowy.appflowy">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
|
||||
@@ -1,42 +1,48 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="io.appflowy.appflowy">
|
||||
<application
|
||||
android:label="appflowy_flutter"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:name="${applicationName}">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<!-- Displays an Android View that continues showing the launch screen
|
||||
Drawable until Flutter paints its first frame, then this splash
|
||||
screen fades out. A splash screen is useful to avoid any visual
|
||||
gap between the end of Android's launch screen and the painting of
|
||||
Flutter's first frame. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.SplashScreenDrawable"
|
||||
android:resource="@drawable/launch_background"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
</application>
|
||||
</manifest>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application android:label="AppFlowy" android:icon="@mipmap/ic_launcher"
|
||||
android:name="${applicationName}">
|
||||
<activity android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize">
|
||||
<!--
|
||||
Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI.
|
||||
-->
|
||||
<meta-data android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme" />
|
||||
<!--
|
||||
Displays an Android View that continues showing the launch screen
|
||||
Drawable until Flutter paints its first frame, then this splash
|
||||
screen fades out. A splash screen is useful to avoid any visual
|
||||
gap between the end of Android's launch screen and the painting of
|
||||
Flutter's first frame.
|
||||
-->
|
||||
<meta-data android:name="io.flutter.embedding.android.SplashScreenDrawable"
|
||||
android:resource="@drawable/launch_background" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
|
||||
<intent-filter android:autoVerify="true">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
<data android:scheme="appflowy-flutter" />
|
||||
<!-- <data android:host="login-callback" /> -->
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!--
|
||||
Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java
|
||||
-->
|
||||
<meta-data android:name="flutterEmbedding" android:value="2" />
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,15 @@
|
||||
cmake_minimum_required(VERSION 3.10.0)
|
||||
|
||||
project(AppFlowy)
|
||||
|
||||
# arm64-v8a
|
||||
file(COPY
|
||||
${ANDROID_NDK}/sources/cxx-stl/llvm-libc++/libs/arm64-v8a/libc++_shared.so
|
||||
DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/jniLibs/arm64-v8a
|
||||
)
|
||||
|
||||
# x86_64
|
||||
file(COPY
|
||||
${ANDROID_NDK}/sources/cxx-stl/llvm-libc++/libs/x86_64/libc++_shared.so
|
||||
DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/jniLibs/x86_64
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int64_t init_sdk(char *path);
|
||||
|
||||
void async_event(int64_t port, const uint8_t *input, uintptr_t len);
|
||||
|
||||
const uint8_t *sync_event(const uint8_t *input, uintptr_t len);
|
||||
|
||||
int32_t set_stream_port(int64_t port);
|
||||
|
||||
void link_me_please(void);
|
||||
|
||||
void backend_log(int64_t level, const char *data);
|
||||
|
||||
void set_env(const char *data);
|
||||
|
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 16 KiB |
@@ -1,5 +1,4 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="io.appflowy.appflowy">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.6.10'
|
||||
ext.kotlin_version = '1.8.0'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||
classpath 'com.android.tools.build:gradle:7.4.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,6 @@ subprojects {
|
||||
project.evaluationDependsOn(':app')
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
tasks.register("clean", Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
@@ -2,3 +2,4 @@ org.gradle.jvmargs=-Xmx1536M
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
org.gradle.caching=true
|
||||
android.suppressUnsupportedCompileSdk=33
|
||||
@@ -1,5 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -0,0 +1 @@
|
||||
use_precompiled_binaries: true
|
||||
@@ -25,8 +25,8 @@ void main() {
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
await tester.tapButton(find.byType(SettingLogoutButton));
|
||||
|
||||
tester.expectToSeeText(LocaleKeys.button_OK.tr());
|
||||
await tester.tapButtonWithName(LocaleKeys.button_OK.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));
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_board/appflowy_board.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../util/util.dart';
|
||||
|
||||
const defaultFirstCardName = 'Card 1';
|
||||
const defaultLastCardName = 'Card 3';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('board add row test', () {
|
||||
testWidgets('Add card from header', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Board);
|
||||
|
||||
final findFirstCard = find.descendant(
|
||||
of: find.byType(AppFlowyGroupCard),
|
||||
matching: find.byType(FlowyText),
|
||||
);
|
||||
|
||||
FlowyText firstCardText = tester.firstWidget(findFirstCard);
|
||||
expect(firstCardText.text, defaultFirstCardName);
|
||||
|
||||
await tester.tap(
|
||||
find
|
||||
.descendant(
|
||||
of: find.byType(AppFlowyGroupHeader),
|
||||
matching: find.byType(FlowySvg),
|
||||
)
|
||||
.first,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
const newCardName = 'Card 4';
|
||||
await tester.enterText(
|
||||
find.descendant(
|
||||
of: find.byType(IntrinsicHeight),
|
||||
matching: find.byType(TextField),
|
||||
),
|
||||
newCardName,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byType(AppFlowyBoard));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
firstCardText = tester.firstWidget(findFirstCard);
|
||||
expect(firstCardText.text, newCardName);
|
||||
});
|
||||
|
||||
testWidgets('Add card from footer', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Board);
|
||||
|
||||
final findLastCard = find.descendant(
|
||||
of: find.byType(AppFlowyGroupCard),
|
||||
matching: find.byType(FlowyText),
|
||||
);
|
||||
|
||||
FlowyText? lastCardText =
|
||||
tester.widgetList(findLastCard).last as FlowyText;
|
||||
expect(lastCardText.text, defaultLastCardName);
|
||||
|
||||
await tester.tap(
|
||||
find
|
||||
.descendant(
|
||||
of: find.byType(AppFlowyGroupFooter),
|
||||
matching: find.byType(FlowySvg),
|
||||
)
|
||||
.first,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
const newCardName = 'Card 4';
|
||||
await tester.enterText(
|
||||
find.descendant(
|
||||
of: find.byType(IntrinsicHeight),
|
||||
matching: find.byType(TextField),
|
||||
),
|
||||
newCardName,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byType(AppFlowyBoard));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
lastCardText = tester.widgetList(findLastCard).last as FlowyText;
|
||||
expect(lastCardText.text, newCardName);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
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;
|
||||
|
||||
void startTesting() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Board integration tests
|
||||
board_row_test.main();
|
||||
board_add_row_test.main();
|
||||
}
|
||||
@@ -174,25 +174,23 @@ void main() {
|
||||
await tester.openCalendarEvent(index: 0, date: sameDayNextWeek);
|
||||
await tester.deleteEventFromEventEditor();
|
||||
|
||||
// Create a new event in today's calendar cell
|
||||
await tester.scrollToToday();
|
||||
await tester.doubleClickCalendarCell(today);
|
||||
// Create another event on the 5th of this month
|
||||
final fifthOfThisMonth = DateTime(today.year, today.month, 5);
|
||||
await tester.doubleClickCalendarCell(fifthOfThisMonth);
|
||||
await tester.dismissEventEditor();
|
||||
|
||||
// Make sure that the event is today
|
||||
tester.assertNumberOfEventsOnSpecificDay(1, today);
|
||||
// Make sure that the event is on the 4t
|
||||
tester.assertNumberOfEventsOnSpecificDay(1, fifthOfThisMonth);
|
||||
|
||||
// Click on the event
|
||||
await tester.openCalendarEvent(index: 0);
|
||||
await tester.openCalendarEvent(index: 0, date: fifthOfThisMonth);
|
||||
|
||||
// Open the date editor of the event
|
||||
await tester.tapDateCellInRowDetailPage();
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Edit the event's date. To avoid selecting a day outside of the current month, the new date will be one day closer to the middle of the month.
|
||||
final newDate = today.day < 15
|
||||
? today.add(const Duration(days: 1))
|
||||
: today.subtract(const Duration(days: 1));
|
||||
// Edit the event's date
|
||||
final newDate = fifthOfThisMonth.add(const Duration(days: 1));
|
||||
await tester.selectDay(content: newDate.day);
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
|
||||
@@ -245,7 +245,7 @@ void main() {
|
||||
await tester.wait(500);
|
||||
|
||||
// Focus on the editor
|
||||
final textBlock = find.byType(TextBlockComponentWidget);
|
||||
final textBlock = find.byType(ParagraphBlockComponentWidget);
|
||||
await tester.tapAt(tester.getCenter(textBlock));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ void main() {
|
||||
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
);
|
||||
// and with one paragraph block
|
||||
expect(find.byType(TextBlockComponentWidget), findsOneWidget);
|
||||
expect(find.byType(ParagraphBlockComponentWidget), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('delete the readme page and restore it', (tester) async {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:appflowy/workspace/application/appearance.dart';
|
||||
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/image/uplo
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart' hide UploadImageMenu;
|
||||
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';
|
||||
@@ -41,8 +42,13 @@ void main() {
|
||||
await tester.editor.tapSlashMenuItemWithName('Image');
|
||||
expect(find.byType(CustomImageBlockComponent), findsOneWidget);
|
||||
expect(find.byType(ImagePlaceholder), findsOneWidget);
|
||||
|
||||
await tester.tapButton(find.byType(ImagePlaceholder));
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(ImagePlaceholder),
|
||||
matching: find.byType(AppFlowyPopover),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.byType(UploadImageMenu), findsOneWidget);
|
||||
|
||||
final image = await rootBundle.load('assets/test/images/sample.jpeg');
|
||||
@@ -84,8 +90,13 @@ void main() {
|
||||
await tester.editor.tapSlashMenuItemWithName('Image');
|
||||
expect(find.byType(CustomImageBlockComponent), findsOneWidget);
|
||||
expect(find.byType(ImagePlaceholder), findsOneWidget);
|
||||
|
||||
await tester.tapButton(find.byType(ImagePlaceholder));
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(ImagePlaceholder),
|
||||
matching: find.byType(AppFlowyPopover),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.byType(UploadImageMenu), findsOneWidget);
|
||||
|
||||
await tester.tapButtonWithName(
|
||||
@@ -133,8 +144,13 @@ void main() {
|
||||
await tester.editor.tapSlashMenuItemWithName('Image');
|
||||
expect(find.byType(CustomImageBlockComponent), findsOneWidget);
|
||||
expect(find.byType(ImagePlaceholder), findsOneWidget);
|
||||
|
||||
await tester.tapButton(find.byType(ImagePlaceholder));
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(ImagePlaceholder),
|
||||
matching: find.byType(AppFlowyPopover),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.byType(UploadImageMenu), findsOneWidget);
|
||||
|
||||
await tester.tapButtonWithName(
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/util.dart';
|
||||
|
||||
// This test is meaningless, just for preventing the CI from failing.
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('Empty', () {
|
||||
testWidgets('toggle theme mode', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
import 'package:appflowy/env/env.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'appearance_settings_test.dart' as appearance_test_runner;
|
||||
import 'auth/auth_test.dart' as auth_test_runner;
|
||||
import 'board/board_test_runner.dart' as board_test_runner;
|
||||
import 'database_calendar_test.dart' as database_calendar_test;
|
||||
import 'database_cell_test.dart' as database_cell_test;
|
||||
import 'database_field_test.dart' as database_field_test;
|
||||
import 'database_field_settings_test.dart' as database_field_settings_test;
|
||||
import 'database_field_test.dart' as database_field_test;
|
||||
import 'database_filter_test.dart' as database_filter_test;
|
||||
import 'database_row_page_test.dart' as database_row_page_test;
|
||||
import 'database_row_test.dart' as database_row_test;
|
||||
@@ -13,17 +16,15 @@ import 'database_share_test.dart' as database_share_test;
|
||||
import 'database_sort_test.dart' as database_sort_test;
|
||||
import 'database_view_test.dart' as database_view_test;
|
||||
import 'document/document_test_runner.dart' as document_test_runner;
|
||||
import 'import_files_test.dart' as import_files_test;
|
||||
import 'share_markdown_test.dart' as share_markdown_test;
|
||||
import 'switch_folder_test.dart' as switch_folder_test;
|
||||
import 'sidebar/sidebar_test_runner.dart' as sidebar_test_runner;
|
||||
import 'board/board_test_runner.dart' as board_test_runner;
|
||||
import 'tabs_test.dart' as tabs_test;
|
||||
import 'empty_test.dart' as first_test;
|
||||
import 'hotkeys_test.dart' as hotkeys_test;
|
||||
import 'appearance_settings_test.dart' as appearance_test_runner;
|
||||
import 'auth/auth_test.dart' as auth_test_runner;
|
||||
import 'import_files_test.dart' as import_files_test;
|
||||
import 'settings/user_icon_test.dart' as user_icon_test;
|
||||
import 'settings/user_language_test.dart' as user_language_test;
|
||||
import 'share_markdown_test.dart' as share_markdown_test;
|
||||
import 'sidebar/sidebar_test_runner.dart' as sidebar_test_runner;
|
||||
import 'switch_folder_test.dart' as switch_folder_test;
|
||||
import 'tabs_test.dart' as tabs_test;
|
||||
|
||||
/// The main task runner for all integration tests in AppFlowy.
|
||||
///
|
||||
@@ -34,6 +35,10 @@ import 'settings/user_language_test.dart' as user_language_test;
|
||||
/// as the test target.
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// This test must be run first, otherwise the CI will fail.
|
||||
first_test.main();
|
||||
|
||||
switch_folder_test.main();
|
||||
share_markdown_test.main();
|
||||
import_files_test.main();
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:appflowy/plugins/document/presentation/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;
|
||||
|
||||
import 'util/mock/mock_file_picker.dart';
|
||||
import 'util/util.dart';
|
||||
|
||||
@@ -15,9 +16,6 @@ void main() {
|
||||
final context = await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// expect to see a readme page
|
||||
tester.expectToSeePageName(gettingStarted);
|
||||
|
||||
// mock the file picker
|
||||
final path = await mockSaveFilePath(
|
||||
p.join(context.applicationDataDirectory, 'test.md'),
|
||||
@@ -78,7 +76,7 @@ void main() {
|
||||
});
|
||||
}
|
||||
|
||||
const expectedMarkdown = r'''
|
||||
const expectedMarkdown = '''
|
||||
# Welcome to AppFlowy!
|
||||
## Here are the basics
|
||||
- [ ] Click anywhere and just start typing.
|
||||
@@ -105,6 +103,15 @@ fn main() {
|
||||
## Have a question❓
|
||||
> Click `?` at the bottom right for help and support.
|
||||
|
||||
> 🥰
|
||||
>
|
||||
> Like AppFlowy? Follow us:
|
||||
> [GitHub](https://github.com/AppFlowy-IO/AppFlowy)
|
||||
> [Twitter](https://twitter.com/appflowy): @appflowy
|
||||
> [Newsletter](https://blog-appflowy.ghost.io/)
|
||||
>
|
||||
|
||||
|
||||
|
||||
|
||||
''';
|
||||
@@ -30,7 +30,7 @@ void main() {
|
||||
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
);
|
||||
// and with one paragraph block
|
||||
expect(find.byType(TextBlockComponentWidget), findsOneWidget);
|
||||
expect(find.byType(ParagraphBlockComponentWidget), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('create a new document, grid, board and calendar',
|
||||
@@ -55,7 +55,7 @@ void main() {
|
||||
switch (layout) {
|
||||
case ViewLayoutPB.Document:
|
||||
// and with one paragraph block
|
||||
expect(find.byType(TextBlockComponentWidget), findsOneWidget);
|
||||
expect(find.byType(ParagraphBlockComponentWidget), findsOneWidget);
|
||||
break;
|
||||
case ViewLayoutPB.Grid:
|
||||
expect(find.byType(GridPage), findsOneWidget);
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/startup/tasks/prelude.dart';
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import 'util/mock/mock_file_picker.dart';
|
||||
import 'util/util.dart';
|
||||
|
||||
@@ -11,6 +14,10 @@ void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('customize the folder path', () {
|
||||
if (Platform.isWindows) {
|
||||
return;
|
||||
}
|
||||
|
||||
testWidgets('switch to B from A, then switch to A again', (tester) async {
|
||||
const userA = 'UserA';
|
||||
const userB = 'UserB';
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/core/config/kv.dart';
|
||||
import 'package:appflowy/core/config/kv_keys.dart';
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
@@ -27,6 +29,10 @@ extension CommonOperations on WidgetTester {
|
||||
Future<void> tapGoButton() async {
|
||||
final goButton = find.byType(GoButton);
|
||||
await tapButton(goButton);
|
||||
|
||||
if (Platform.isWindows) {
|
||||
await pumpAndSettle(const Duration(milliseconds: 200));
|
||||
}
|
||||
}
|
||||
|
||||
/// Tap the + button on the home page.
|
||||
@@ -230,7 +236,7 @@ extension CommonOperations on WidgetTester {
|
||||
final okButton = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is PrimaryTextButton &&
|
||||
widget.label == LocaleKeys.button_OK.tr(),
|
||||
widget.label == LocaleKeys.button_ok.tr(),
|
||||
);
|
||||
await tapButton(okButton);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar
|
||||
import 'package:appflowy/plugins/database_view/tar_bar/tab_bar_header.dart';
|
||||
import 'package:appflowy/plugins/database_view/tar_bar/tar_bar_add_button.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/database_layout_ext.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/field/grid_property.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/setting/setting_property_list.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/accessory/cell_accessory.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/cells.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_editor.dart';
|
||||
@@ -52,7 +52,6 @@ import 'package:appflowy/plugins/database_view/widgets/row/row_banner.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/row_detail.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/row_document.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/row_property.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/setting/database_setting.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/setting/setting_button.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/emoji_picker/emoji_menu_item.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
|
||||
@@ -530,7 +529,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
|
||||
final widget = this.widget<ChecklistItem>(task);
|
||||
assert(
|
||||
widget.option.data.name == name && widget.option.isSelected == isChecked,
|
||||
widget.task.data.name == name && widget.task.isSelected == isChecked,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -748,7 +747,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
final field = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is PrimaryTextButton &&
|
||||
widget.label == LocaleKeys.button_OK.tr(),
|
||||
widget.label == LocaleKeys.button_ok.tr(),
|
||||
);
|
||||
await tapButton(field);
|
||||
}
|
||||
@@ -1132,7 +1131,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
|
||||
/// Should call [tapDatabaseSettingButton] first.
|
||||
Future<void> tapViewPropertiesButton() async {
|
||||
final findSettingItem = find.byType(DatabaseSettingItem);
|
||||
final findSettingItem = find.byType(DatabaseSettingListPopover);
|
||||
final findLayoutButton = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is FlowyText &&
|
||||
@@ -1149,7 +1148,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
|
||||
/// Should call [tapDatabaseSettingButton] first.
|
||||
Future<void> tapDatabaseLayoutButton() async {
|
||||
final findSettingItem = find.byType(DatabaseSettingItem);
|
||||
final findSettingItem = find.byType(DatabaseSettingListPopover);
|
||||
final findLayoutButton = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is FlowyText &&
|
||||
@@ -1165,7 +1164,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
|
||||
Future<void> tapCalendarLayoutSettingButton() async {
|
||||
final findSettingItem = find.byType(DatabaseSettingItem);
|
||||
final findSettingItem = find.byType(DatabaseSettingListPopover);
|
||||
final findLayoutButton = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is FlowyText &&
|
||||
@@ -1302,7 +1301,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await doubleTapAt(location);
|
||||
}
|
||||
|
||||
Future<void> openCalendarEvent({required index, DateTime? date}) async {
|
||||
Future<void> openCalendarEvent({required int index, DateTime? date}) async {
|
||||
final findDayCell = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is CalendarDayCard &&
|
||||
@@ -1433,7 +1432,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
final field = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is PrimaryTextButton &&
|
||||
widget.label == LocaleKeys.button_OK.tr(),
|
||||
widget.label == LocaleKeys.button_ok.tr(),
|
||||
);
|
||||
await tapButton(field);
|
||||
}
|
||||
@@ -1453,7 +1452,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
final okButton = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is PrimaryTextButton &&
|
||||
widget.label == LocaleKeys.button_OK.tr(),
|
||||
widget.label == LocaleKeys.button_ok.tr(),
|
||||
);
|
||||
await tapButton(okButton);
|
||||
}
|
||||
@@ -1505,7 +1504,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
) async {
|
||||
final field = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is GridPropertyCell && widget.fieldInfo.name == fieldName,
|
||||
widget is DatabasePropertyCell && widget.fieldInfo.name == fieldName,
|
||||
);
|
||||
final toggleVisibilityButton =
|
||||
find.descendant(of: field, matching: find.byType(FlowyIconButton));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/application/appearance.dart';
|
||||
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/settings_dialog.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu_element.dart';
|
||||
|
||||
@@ -147,7 +147,7 @@ EXTERNAL SOURCES:
|
||||
SPEC CHECKSUMS:
|
||||
app_links: 5ef33d0d295a89d9d16bb81b0e3b0d5f70d6c875
|
||||
appflowy_backend: 144c20d8bfb298c4e10fa3fa6701a9f41bf98b88
|
||||
connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a
|
||||
connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d
|
||||
device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea
|
||||
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
|
||||
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
|
||||
@@ -164,12 +164,12 @@ SPEC CHECKSUMS:
|
||||
SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84
|
||||
shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c
|
||||
sign_in_with_apple: f3bf75217ea4c2c8b91823f225d70230119b8440
|
||||
super_native_extensions: 49e897b6039bb784226e7354e502a3c68e1659b5
|
||||
super_native_extensions: 4916b3c627a9c7fffdc48a23a9eca0b1ac228fa7
|
||||
SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
|
||||
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
|
||||
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
|
||||
webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a
|
||||
|
||||
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
|
||||
PODFILE CHECKSUM: 8c681999c7764593c94846b2a64b44d86f7a27ac
|
||||
|
||||
COCOAPODS: 1.11.3
|
||||
COCOAPODS: 1.12.1
|
||||
@@ -13,22 +13,9 @@
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
9D1D47ADD7F5DE8237063BCA /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 197F72694BED43249F1523E8 /* Pods_Runner.framework */; };
|
||||
FB3C2A642AE0D57700490715 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 197F72694BED43249F1523E8 /* Pods_Runner.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||
@@ -47,6 +34,7 @@
|
||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
FBE00AD62AE8E46A006B563F /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -54,7 +42,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9D1D47ADD7F5DE8237063BCA /* Pods_Runner.framework in Frameworks */,
|
||||
FB3C2A642AE0D57700490715 /* Pods_Runner.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -102,6 +90,7 @@
|
||||
97C146F01CF9000F007C117D /* Runner */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FBE00AD62AE8E46A006B563F /* Runner.entitlements */,
|
||||
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
||||
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||
@@ -121,7 +110,6 @@
|
||||
580A1ED8E012CA1552E5EFD3 /* Pods-Runner.release.xcconfig */,
|
||||
4C2CB38DA64605A62D45B098 /* Pods-Runner.profile.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -137,7 +125,6 @@
|
||||
97C146EA1CF9000F007C117D /* Sources */,
|
||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||
97C146EC1CF9000F007C117D /* Resources */,
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
08FAA63113168DEC7FB74204 /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
@@ -358,14 +345,26 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.appflowy.appflowy;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = AppFlowy;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.appflowy.flutter;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
STRIP_STYLE = "non-global";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Profile;
|
||||
@@ -482,15 +481,27 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.appflowy.appflowy;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = AppFlowy;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.appflowy.flutter;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
STRIP_STYLE = "non-global";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -501,14 +512,26 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = io.appflowy.appflowy;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = AppFlowy;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.appflowy.flutter;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
STRIP_STYLE = "non-global";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
|
||||
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
@@ -1,122 +1 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-20x20@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-20x20@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-40x40@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-40x40@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-60x60@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-60x60@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-20x20@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-20x20@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-29x29@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-29x29@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-40x40@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-40x40@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-76x76@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-76x76@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "83.5x83.5",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-83.5x83.5@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "1024x1024",
|
||||
"idiom" : "ios-marketing",
|
||||
"filename" : "Icon-App-1024x1024@1x.png",
|
||||
"scale" : "1x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"}]}
|
||||
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 564 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
@@ -2,6 +2,10 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>This app requires access to the photo library.</string>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
@@ -10,12 +14,18 @@
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleLocalizations</key>
|
||||
<array>
|
||||
<string>en</string>
|
||||
</array>
|
||||
<key>CFBundleName</key>
|
||||
<string>appflowy_flutter</string>
|
||||
<string>AppFlowy</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
@@ -27,12 +37,12 @@
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
@@ -52,13 +62,5 @@
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>CFBundleLocalizations</key>
|
||||
<array>
|
||||
<string>en</string>
|
||||
</array>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>aps-environment</key>
|
||||
<string>development</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -27,8 +27,8 @@ class NetworkListener {
|
||||
return _updateConnectionStatus(result);
|
||||
}
|
||||
|
||||
void stop() {
|
||||
_connectivitySubscription.cancel();
|
||||
Future<void> stop() async {
|
||||
await _connectivitySubscription.cancel();
|
||||
}
|
||||
|
||||
Future<void> _updateConnectionStatus(ConnectivityResult result) async {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import 'package:appflowy/mobile/presentation/database/mobile_board_screen.dart';
|
||||
import 'package:appflowy/mobile/presentation/database/mobile_calendar_screen.dart';
|
||||
import 'package:appflowy/mobile/presentation/database/mobile_grid_screen.dart';
|
||||
import 'package:appflowy/mobile/presentation/presentation.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
extension MobileRouter on BuildContext {
|
||||
Future<void> pushView(ViewPB view) async {
|
||||
push(
|
||||
Uri(
|
||||
path: view.routeName,
|
||||
queryParameters: view.queryParameters,
|
||||
).toString(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension on ViewPB {
|
||||
String get routeName {
|
||||
switch (layout) {
|
||||
case ViewLayoutPB.Document:
|
||||
return MobileEditorScreen.routeName;
|
||||
case ViewLayoutPB.Grid:
|
||||
return MobileGridScreen.routeName;
|
||||
case ViewLayoutPB.Calendar:
|
||||
return MobileCalendarScreen.routeName;
|
||||
case ViewLayoutPB.Board:
|
||||
return MobileBoardScreen.routeName;
|
||||
default:
|
||||
throw UnimplementedError('routeName for $this is not implemented');
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> get queryParameters {
|
||||
switch (layout) {
|
||||
case ViewLayoutPB.Document:
|
||||
return {
|
||||
MobileEditorScreen.viewId: id,
|
||||
MobileEditorScreen.viewTitle: name,
|
||||
};
|
||||
case ViewLayoutPB.Grid:
|
||||
return {
|
||||
MobileGridScreen.viewId: id,
|
||||
MobileGridScreen.viewTitle: name,
|
||||
};
|
||||
case ViewLayoutPB.Calendar:
|
||||
return {
|
||||
MobileCalendarScreen.viewId: id,
|
||||
MobileCalendarScreen.viewTitle: name,
|
||||
};
|
||||
case ViewLayoutPB.Board:
|
||||
return {
|
||||
MobileBoardScreen.viewId: id,
|
||||
MobileBoardScreen.viewTitle: name,
|
||||
};
|
||||
default:
|
||||
throw UnimplementedError(
|
||||
'queryParameters for $this is not implemented',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,98 @@
|
||||
// ThemeData in mobile
|
||||
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
||||
import 'package:flowy_infra/colorscheme/colorscheme.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
ThemeData getMobileThemeData() {
|
||||
const mobileColorTheme = ColorScheme(
|
||||
brightness: Brightness.light,
|
||||
primary: Color(0xFF2DA2F6), //primary 100
|
||||
onPrimary: Colors.white,
|
||||
// TODO(yijing): add color later
|
||||
secondary: Colors.white,
|
||||
onSecondary: Colors.white,
|
||||
error: Color(0xffFB006D),
|
||||
onError: Color(0xffFB006D),
|
||||
background: Colors.white,
|
||||
onBackground: Color(0xff2F3030), // title text
|
||||
outline: Color(0xffBDC0C5), //caption
|
||||
//Snack bar
|
||||
surface: Colors.white,
|
||||
onSurface: Color(0xff2F3030), // title text
|
||||
);
|
||||
const _primaryColor = Color(0xFF2DA2F6); //primary 100
|
||||
const _onBackgroundColor = Color(0xff2F3030); // text/title color
|
||||
const _onSurfaceColor = Color(0xff676666); // text/body color
|
||||
const _onSecondaryColor = Color(0xFFC5C7CB); // text/body2 color
|
||||
|
||||
// TODO(yijing): improve theme color before release
|
||||
ThemeData getMobileThemeData(
|
||||
Brightness brightness,
|
||||
FlowyColorScheme theme,
|
||||
String fontFamily,
|
||||
String monospaceFontFamily,
|
||||
) {
|
||||
final mobileColorTheme = (brightness == Brightness.light)
|
||||
? ColorScheme(
|
||||
brightness: brightness,
|
||||
primary: _primaryColor,
|
||||
onPrimary: Colors.white,
|
||||
// TODO(yijing): add color later
|
||||
secondary: Colors.white,
|
||||
onSecondary: _onSecondaryColor,
|
||||
error: const Color(0xffFB006D),
|
||||
onError: const Color(0xffFB006D),
|
||||
background: Colors.white,
|
||||
onBackground: _onBackgroundColor,
|
||||
outline: const Color(0xffBDC0C5), //caption
|
||||
outlineVariant: const Color(0xffCBD5E0).withOpacity(0.24),
|
||||
//Snack bar
|
||||
surface: Colors.white,
|
||||
onSurface: _onSurfaceColor, // text/body color
|
||||
)
|
||||
: ColorScheme(
|
||||
brightness: brightness,
|
||||
primary: _primaryColor,
|
||||
onPrimary: Colors.white,
|
||||
// TODO(yijing): add color later
|
||||
secondary: Colors.black,
|
||||
onSecondary: Colors.white,
|
||||
error: const Color(0xffFB006D),
|
||||
onError: const Color(0xffFB006D),
|
||||
background: const Color(0xff1C1C1E), // BG/Secondary color
|
||||
onBackground: Colors.white,
|
||||
outline: const Color(0xff96989C), //caption
|
||||
outlineVariant: Colors.black,
|
||||
//Snack bar
|
||||
surface: const Color(0xff2F3030),
|
||||
onSurface: const Color(0xffC5C6C7), // text/body color
|
||||
);
|
||||
|
||||
return ThemeData(
|
||||
// color
|
||||
primaryColor: mobileColorTheme.primary, //primary 100
|
||||
primaryColorLight: const Color(0xFF57B5F8), //primary 80
|
||||
dividerColor: mobileColorTheme.outline, //caption
|
||||
hintColor: mobileColorTheme.outline,
|
||||
disabledColor: mobileColorTheme.outline,
|
||||
scaffoldBackgroundColor: mobileColorTheme.background,
|
||||
appBarTheme: AppBarTheme(
|
||||
foregroundColor: mobileColorTheme.onBackground,
|
||||
backgroundColor: mobileColorTheme.background,
|
||||
elevation: 0,
|
||||
elevation: 80,
|
||||
centerTitle: false,
|
||||
titleTextStyle: TextStyle(
|
||||
fontFamily: 'Poppins',
|
||||
color: mobileColorTheme.onBackground,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: 0.05,
|
||||
),
|
||||
shadowColor: mobileColorTheme.outlineVariant,
|
||||
),
|
||||
radioTheme: RadioThemeData(
|
||||
fillColor: MaterialStateProperty.resolveWith((states) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
return mobileColorTheme.primary;
|
||||
}
|
||||
return mobileColorTheme.outline;
|
||||
}),
|
||||
),
|
||||
// button
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
fixedSize: MaterialStateProperty.all(const Size.fromHeight(48)),
|
||||
elevation: MaterialStateProperty.all(0),
|
||||
textStyle: MaterialStateProperty.all(
|
||||
const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
shadowColor: MaterialStateProperty.all(null),
|
||||
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||
(Set<MaterialState> states) {
|
||||
@@ -55,6 +107,12 @@ ThemeData getMobileThemeData() {
|
||||
),
|
||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
textStyle: MaterialStateProperty.all(
|
||||
const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
foregroundColor: MaterialStateProperty.all(
|
||||
mobileColorTheme.onBackground,
|
||||
),
|
||||
@@ -79,10 +137,19 @@ ThemeData getMobileThemeData() {
|
||||
),
|
||||
),
|
||||
),
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
textStyle: MaterialStateProperty.all(
|
||||
const TextStyle(
|
||||
fontFamily: 'Poppins',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// text
|
||||
fontFamily: 'Poppins',
|
||||
textTheme: const TextTheme(
|
||||
displayLarge: TextStyle(
|
||||
textTheme: TextTheme(
|
||||
displayLarge: const TextStyle(
|
||||
color: Color(0xFF57B5F8),
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.w700,
|
||||
@@ -90,7 +157,7 @@ ThemeData getMobileThemeData() {
|
||||
letterSpacing: 0.16,
|
||||
),
|
||||
displayMedium: TextStyle(
|
||||
color: Color(0xff2F3030),
|
||||
color: mobileColorTheme.onBackground,
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 1.20,
|
||||
@@ -98,33 +165,46 @@ ThemeData getMobileThemeData() {
|
||||
),
|
||||
// H1 Semi 26
|
||||
displaySmall: TextStyle(
|
||||
color: Color(0xFF2F3030),
|
||||
fontSize: 26,
|
||||
color: mobileColorTheme.onBackground,
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 1.10,
|
||||
letterSpacing: 0.13,
|
||||
),
|
||||
// body2 14 Regular
|
||||
bodyMedium: TextStyle(
|
||||
color: Color(0xFFC5C7CB),
|
||||
color: mobileColorTheme.onBackground,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
height: 1.20,
|
||||
letterSpacing: 0.07,
|
||||
),
|
||||
// blue text button
|
||||
// Trash empty title
|
||||
labelLarge: TextStyle(
|
||||
color: mobileColorTheme.onBackground,
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.3,
|
||||
),
|
||||
// setting item title
|
||||
labelMedium: TextStyle(
|
||||
color: Color(0xFF2DA2F6),
|
||||
fontSize: 14,
|
||||
color: mobileColorTheme.onSurface,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
height: 1.20,
|
||||
),
|
||||
// setting group title
|
||||
labelSmall: TextStyle(
|
||||
color: mobileColorTheme.onBackground,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: 0.05,
|
||||
),
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
contentPadding: const EdgeInsets.all(8),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
width: 2,
|
||||
color: Color(0xFF2DA2F6), //primary 100
|
||||
color: _primaryColor,
|
||||
),
|
||||
borderRadius: BorderRadius.all(Radius.circular(6)),
|
||||
),
|
||||
@@ -136,13 +216,54 @@ ThemeData getMobileThemeData() {
|
||||
borderSide: BorderSide(color: mobileColorTheme.error),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(6)),
|
||||
),
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Color(0xffBDC0C5), //caption
|
||||
color: mobileColorTheme.outline,
|
||||
),
|
||||
borderRadius: BorderRadius.all(Radius.circular(6)),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(6)),
|
||||
),
|
||||
),
|
||||
colorScheme: mobileColorTheme,
|
||||
extensions: [
|
||||
AFThemeExtension(
|
||||
warning: theme.yellow,
|
||||
success: theme.green,
|
||||
tint1: theme.tint1,
|
||||
tint2: theme.tint2,
|
||||
tint3: theme.tint3,
|
||||
tint4: theme.tint4,
|
||||
tint5: theme.tint5,
|
||||
tint6: theme.tint6,
|
||||
tint7: theme.tint7,
|
||||
tint8: theme.tint8,
|
||||
tint9: theme.tint9,
|
||||
textColor: theme.text,
|
||||
greyHover: theme.hoverBG1,
|
||||
greySelect: theme.bg3,
|
||||
lightGreyHover: theme.hoverBG3,
|
||||
toggleOffFill: theme.shader5,
|
||||
progressBarBGColor: theme.progressBarBGColor,
|
||||
toggleButtonBGColor: theme.toggleButtonBGColor,
|
||||
calendarWeekendBGColor: theme.calendarWeekendBGColor,
|
||||
gridRowCountColor: theme.gridRowCountColor,
|
||||
code: getFontStyle(
|
||||
fontFamily: monospaceFontFamily,
|
||||
fontColor: theme.shader3,
|
||||
),
|
||||
callout: getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: FontSizes.s11,
|
||||
fontColor: theme.shader3,
|
||||
),
|
||||
calloutBGColor: theme.hoverBG3,
|
||||
tableCellBGColor: theme.surface,
|
||||
caption: getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: FontSizes.s11,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontColor: theme.hint,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AppBarBackButton extends StatelessWidget {
|
||||
const AppBarBackButton({
|
||||
super.key,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppBarButton(
|
||||
onTap: onTap,
|
||||
child: const Icon(Icons.arrow_back_ios_new),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppBarMoreButton extends StatelessWidget {
|
||||
const AppBarMoreButton({
|
||||
super.key,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
final void Function(BuildContext context) onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppBarButton(
|
||||
onTap: () => onTap(context),
|
||||
child: const Icon(
|
||||
// replace with flowy icon
|
||||
Icons.more_horiz_sharp,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppBarButton extends StatelessWidget {
|
||||
const AppBarButton({
|
||||
super.key,
|
||||
this.extent = 16.0,
|
||||
required this.onTap,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
// used to extend the hit area of the more button
|
||||
final double extent;
|
||||
|
||||
final VoidCallback onTap;
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
enableFeedback: true,
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(extent),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FlowyBoxContainer extends StatelessWidget {
|
||||
const FlowyBoxContainer({
|
||||
super.key,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: 6.0,
|
||||
vertical: 8.0,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.onSecondary,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/app_bar_actions.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_view_page.dart';
|
||||
import 'package:appflowy/mobile/presentation/error/error_page.dart';
|
||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:dartz/dartz.dart' hide State;
|
||||
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:go_router/go_router.dart';
|
||||
|
||||
class MobileViewPage extends StatefulWidget {
|
||||
const MobileViewPage({
|
||||
super.key,
|
||||
required this.id,
|
||||
this.title,
|
||||
required this.viewLayout,
|
||||
});
|
||||
|
||||
/// view id
|
||||
final String id;
|
||||
final String? title;
|
||||
final ViewLayoutPB viewLayout;
|
||||
|
||||
@override
|
||||
State<MobileViewPage> createState() => _MobileViewPageState();
|
||||
}
|
||||
|
||||
class _MobileViewPageState extends State<MobileViewPage> {
|
||||
late final Future<Either<ViewPB, FlowyError>> future;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
future = ViewBackendService.getView(widget.id);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: future,
|
||||
builder: (context, state) {
|
||||
Widget body;
|
||||
ViewPB? viewPB;
|
||||
final actions = <Widget>[];
|
||||
if (state.connectionState != ConnectionState.done) {
|
||||
body = const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
} else if (!state.hasData) {
|
||||
body = MobileErrorPage(
|
||||
message: LocaleKeys.error_loadingViewError.tr(),
|
||||
);
|
||||
} else {
|
||||
body = state.data!.fold((view) {
|
||||
viewPB = view;
|
||||
actions.add(_buildAppBarMoreButton(view));
|
||||
return view.plugin().widgetBuilder.buildWidget(shrinkWrap: false);
|
||||
}, (error) {
|
||||
return MobileErrorPage(
|
||||
message: error.toString(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (viewPB != null) {
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
create: (_) =>
|
||||
FavoriteBloc()..add(const FavoriteEvent.initial()),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (_) =>
|
||||
ViewBloc(view: viewPB!)..add(const ViewEvent.initial()),
|
||||
),
|
||||
],
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final view = context.watch<ViewBloc>().state.view;
|
||||
return _buildApp(
|
||||
view,
|
||||
actions,
|
||||
body,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return _buildApp(
|
||||
null,
|
||||
[],
|
||||
body,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildApp(ViewPB? view, List<Widget> actions, Widget child) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
titleSpacing: 0,
|
||||
title: FlowyText.semibold(
|
||||
view?.name ?? widget.title ?? '',
|
||||
fontSize: 14.0,
|
||||
),
|
||||
leading: AppBarBackButton(
|
||||
onTap: () => context.pop(),
|
||||
),
|
||||
actions: actions,
|
||||
),
|
||||
body: SafeArea(
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAppBarMoreButton(ViewPB view) {
|
||||
return AppBarMoreButton(
|
||||
onTap: (context) {
|
||||
showMobileBottomSheet(
|
||||
context: context,
|
||||
builder: (_) => _buildViewPageBottomSheet(context),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildViewPageBottomSheet(BuildContext context) {
|
||||
final view = context.read<ViewBloc>().state.view;
|
||||
return ViewPageBottomSheet(
|
||||
view: view,
|
||||
onAction: (action) {
|
||||
switch (action) {
|
||||
case MobileViewBottomSheetBodyAction.duplicate:
|
||||
context.pop();
|
||||
context.read<ViewBloc>().add(const ViewEvent.duplicate());
|
||||
// show toast
|
||||
break;
|
||||
case MobileViewBottomSheetBodyAction.share:
|
||||
// unimplemented
|
||||
context.pop();
|
||||
break;
|
||||
case MobileViewBottomSheetBodyAction.delete:
|
||||
// pop to home page
|
||||
context
|
||||
..pop()
|
||||
..pop();
|
||||
context.read<ViewBloc>().add(const ViewEvent.delete());
|
||||
break;
|
||||
case MobileViewBottomSheetBodyAction.addToFavorites:
|
||||
case MobileViewBottomSheetBodyAction.removeFromFavorites:
|
||||
context.pop();
|
||||
context.read<FavoriteBloc>().add(FavoriteEvent.toggle(view));
|
||||
break;
|
||||
case MobileViewBottomSheetBodyAction.undo:
|
||||
case MobileViewBottomSheetBodyAction.redo:
|
||||
case MobileViewBottomSheetBodyAction.helpCenter:
|
||||
// unimplemented
|
||||
context.pop();
|
||||
break;
|
||||
case MobileViewBottomSheetBodyAction.rename:
|
||||
throw UnimplementedError();
|
||||
}
|
||||
},
|
||||
onRename: (name) {
|
||||
if (name != view.name) {
|
||||
context.read<ViewBloc>().add(ViewEvent.rename(name));
|
||||
}
|
||||
context.pop();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_drag_handler.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_rename_widget.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_view_item_body.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_view_item_header.dart';
|
||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart' hide WidgetBuilder;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
Future<void> showMobileBottomSheet({
|
||||
required BuildContext context,
|
||||
required WidgetBuilder builder,
|
||||
}) async {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
enableDrag: true,
|
||||
useSafeArea: true,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(8.0),
|
||||
topRight: Radius.circular(8.0),
|
||||
),
|
||||
),
|
||||
builder: builder,
|
||||
);
|
||||
}
|
||||
|
||||
enum MobileBottomSheetType {
|
||||
view,
|
||||
rename,
|
||||
}
|
||||
|
||||
class MobileViewItemBottomSheet extends StatefulWidget {
|
||||
const MobileViewItemBottomSheet({
|
||||
super.key,
|
||||
required this.view,
|
||||
this.defaultType = MobileBottomSheetType.view,
|
||||
});
|
||||
|
||||
final ViewPB view;
|
||||
final MobileBottomSheetType defaultType;
|
||||
|
||||
@override
|
||||
State<MobileViewItemBottomSheet> createState() =>
|
||||
_MobileViewItemBottomSheetState();
|
||||
}
|
||||
|
||||
class _MobileViewItemBottomSheetState extends State<MobileViewItemBottomSheet> {
|
||||
MobileBottomSheetType type = MobileBottomSheetType.view;
|
||||
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
|
||||
type = widget.defaultType;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// drag handler
|
||||
const MobileBottomSheetDragHandler(),
|
||||
|
||||
// header
|
||||
_buildHeader(),
|
||||
const VSpace(8.0),
|
||||
const Divider(),
|
||||
|
||||
// body
|
||||
_buildBody(),
|
||||
const VSpace(12.0),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
switch (type) {
|
||||
case MobileBottomSheetType.view:
|
||||
case MobileBottomSheetType.rename:
|
||||
// header
|
||||
return MobileViewItemBottomSheetHeader(
|
||||
showBackButton: type != MobileBottomSheetType.view,
|
||||
view: widget.view,
|
||||
onBack: () {
|
||||
setState(() {
|
||||
type = MobileBottomSheetType.view;
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildBody() {
|
||||
switch (type) {
|
||||
case MobileBottomSheetType.view:
|
||||
return MobileViewItemBottomSheetBody(
|
||||
isFavorite: widget.view.isFavorite,
|
||||
onAction: (action) {
|
||||
switch (action) {
|
||||
case MobileViewItemBottomSheetBodyAction.rename:
|
||||
setState(() {
|
||||
type = MobileBottomSheetType.rename;
|
||||
});
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.duplicate:
|
||||
context.pop();
|
||||
context.read<ViewBloc>().add(const ViewEvent.duplicate());
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.share:
|
||||
// unimplemented
|
||||
context.pop();
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.delete:
|
||||
context.pop();
|
||||
context.read<ViewBloc>().add(const ViewEvent.delete());
|
||||
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.addToFavorites:
|
||||
case MobileViewItemBottomSheetBodyAction.removeFromFavorites:
|
||||
context.pop();
|
||||
context
|
||||
.read<FavoriteBloc>()
|
||||
.add(FavoriteEvent.toggle(widget.view));
|
||||
break;
|
||||
}
|
||||
},
|
||||
);
|
||||
case MobileBottomSheetType.rename:
|
||||
return MobileBottomSheetRenameWidget(
|
||||
name: widget.view.name,
|
||||
onRename: (name) {
|
||||
if (name != widget.view.name) {
|
||||
context.read<ViewBloc>().add(ViewEvent.rename(name));
|
||||
}
|
||||
context.pop();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/box_container.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class BottomSheetActionWidget extends StatelessWidget {
|
||||
const BottomSheetActionWidget({
|
||||
super.key,
|
||||
required this.svg,
|
||||
required this.text,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
final FlowySvgData svg;
|
||||
final String text;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FlowyBoxContainer(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
HapticFeedback.mediumImpact();
|
||||
onTap();
|
||||
},
|
||||
enableFeedback: true,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10.0,
|
||||
horizontal: 12.0,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
FlowySvg(
|
||||
svg,
|
||||
size: const Size.square(24.0),
|
||||
blendMode: BlendMode.dst,
|
||||
),
|
||||
const HSpace(6.0),
|
||||
FlowyText(text),
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
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_action_widget.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_drag_handler.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_view_item_header.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AddNewPageWidgetBottomSheet extends StatelessWidget {
|
||||
const AddNewPageWidgetBottomSheet({
|
||||
super.key,
|
||||
required this.view,
|
||||
required this.onAction,
|
||||
});
|
||||
|
||||
final ViewPB view;
|
||||
final void Function(ViewLayoutPB layout) onAction;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// drag handler
|
||||
const MobileBottomSheetDragHandler(),
|
||||
|
||||
// header
|
||||
MobileViewItemBottomSheetHeader(
|
||||
showBackButton: false,
|
||||
view: view,
|
||||
onBack: () {},
|
||||
),
|
||||
const VSpace(8.0),
|
||||
const Divider(),
|
||||
|
||||
// body
|
||||
_AddNewPageBody(
|
||||
onAction: onAction,
|
||||
),
|
||||
const VSpace(24.0),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AddNewPageBody extends StatelessWidget {
|
||||
const _AddNewPageBody({
|
||||
required this.onAction,
|
||||
});
|
||||
|
||||
final void Function(ViewLayoutPB layout) onAction;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
// rename, duplicate
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.documents_s,
|
||||
text: LocaleKeys.document_menuName.tr(),
|
||||
onTap: () => onAction(ViewLayoutPB.Document),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.grid_s,
|
||||
text: LocaleKeys.grid_menuName.tr(),
|
||||
onTap: () => onAction(ViewLayoutPB.Grid),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// share, delete
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.board_s,
|
||||
text: LocaleKeys.board_menuName.tr(),
|
||||
onTap: () => onAction(ViewLayoutPB.Board),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.date_s,
|
||||
text: LocaleKeys.calendar_menuName.tr(),
|
||||
onTap: () => onAction(ViewLayoutPB.Calendar),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MobileBottomSheetDragHandler extends StatelessWidget {
|
||||
const MobileBottomSheetDragHandler({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||
child: Container(
|
||||
width: 60,
|
||||
height: 4,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(2.0),
|
||||
color: Theme.of(context).colorScheme.onSecondary,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MobileBottomSheetRenameWidget extends StatefulWidget {
|
||||
const MobileBottomSheetRenameWidget({
|
||||
super.key,
|
||||
required this.name,
|
||||
required this.onRename,
|
||||
});
|
||||
|
||||
final String name;
|
||||
final void Function(String name) onRename;
|
||||
|
||||
@override
|
||||
State<MobileBottomSheetRenameWidget> createState() =>
|
||||
_MobileBottomSheetRenameWidgetState();
|
||||
}
|
||||
|
||||
class _MobileBottomSheetRenameWidgetState
|
||||
extends State<MobileBottomSheetRenameWidget> {
|
||||
late final TextEditingController controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
controller = TextEditingController(text: widget.name);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
controller.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4.0,
|
||||
vertical: 16.0,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const HSpace(8.0),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 44.0,
|
||||
child: FlowyTextField(
|
||||
controller: controller,
|
||||
),
|
||||
),
|
||||
),
|
||||
const HSpace(12.0),
|
||||
FlowyTextButton(
|
||||
LocaleKeys.button_edit.tr(),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 12.0,
|
||||
horizontal: 16.0,
|
||||
),
|
||||
fontColor: Colors.white,
|
||||
fillColor: Colors.lightBlue.shade300,
|
||||
onPressed: () {
|
||||
widget.onRename(controller.text);
|
||||
},
|
||||
),
|
||||
const HSpace(8.0),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_drag_handler.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_rename_widget.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_view_item_body.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_view_item_header.dart';
|
||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart' hide WidgetBuilder;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
enum MobileBottomSheetType {
|
||||
view,
|
||||
rename,
|
||||
}
|
||||
|
||||
class MobileViewItemBottomSheet extends StatefulWidget {
|
||||
const MobileViewItemBottomSheet({
|
||||
super.key,
|
||||
required this.view,
|
||||
this.defaultType = MobileBottomSheetType.view,
|
||||
});
|
||||
|
||||
final ViewPB view;
|
||||
final MobileBottomSheetType defaultType;
|
||||
|
||||
@override
|
||||
State<MobileViewItemBottomSheet> createState() =>
|
||||
_MobileViewItemBottomSheetState();
|
||||
}
|
||||
|
||||
class _MobileViewItemBottomSheetState extends State<MobileViewItemBottomSheet> {
|
||||
MobileBottomSheetType type = MobileBottomSheetType.view;
|
||||
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
|
||||
type = widget.defaultType;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// drag handler
|
||||
const MobileBottomSheetDragHandler(),
|
||||
|
||||
// header
|
||||
_buildHeader(),
|
||||
const VSpace(8.0),
|
||||
const Divider(),
|
||||
|
||||
// body
|
||||
_buildBody(),
|
||||
const VSpace(24.0),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
switch (type) {
|
||||
case MobileBottomSheetType.view:
|
||||
case MobileBottomSheetType.rename:
|
||||
// header
|
||||
return MobileViewItemBottomSheetHeader(
|
||||
showBackButton: type != MobileBottomSheetType.view,
|
||||
view: widget.view,
|
||||
onBack: () {
|
||||
setState(() {
|
||||
type = MobileBottomSheetType.view;
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildBody() {
|
||||
switch (type) {
|
||||
case MobileBottomSheetType.view:
|
||||
return MobileViewItemBottomSheetBody(
|
||||
isFavorite: widget.view.isFavorite,
|
||||
onAction: (action) {
|
||||
switch (action) {
|
||||
case MobileViewItemBottomSheetBodyAction.rename:
|
||||
setState(() {
|
||||
type = MobileBottomSheetType.rename;
|
||||
});
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.duplicate:
|
||||
context.read<ViewBloc>().add(const ViewEvent.duplicate());
|
||||
context.pop();
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.share:
|
||||
// unimplemented
|
||||
context.pop();
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.delete:
|
||||
context.read<ViewBloc>().add(const ViewEvent.delete());
|
||||
context.pop();
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.addToFavorites:
|
||||
case MobileViewItemBottomSheetBodyAction.removeFromFavorites:
|
||||
context
|
||||
.read<FavoriteBloc>()
|
||||
.add(FavoriteEvent.toggle(widget.view));
|
||||
context.pop();
|
||||
break;
|
||||
}
|
||||
},
|
||||
);
|
||||
case MobileBottomSheetType.rename:
|
||||
return MobileBottomSheetRenameWidget(
|
||||
name: widget.view.name,
|
||||
onRename: (name) {
|
||||
if (name != widget.view.name) {
|
||||
context.read<ViewBloc>().add(ViewEvent.rename(name));
|
||||
}
|
||||
context.pop();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
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_action_widget.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum MobileViewItemBottomSheetBodyAction {
|
||||
rename,
|
||||
duplicate,
|
||||
share,
|
||||
delete,
|
||||
addToFavorites,
|
||||
removeFromFavorites,
|
||||
}
|
||||
|
||||
class MobileViewItemBottomSheetBody extends StatelessWidget {
|
||||
const MobileViewItemBottomSheetBody({
|
||||
super.key,
|
||||
this.isFavorite = false,
|
||||
required this.onAction,
|
||||
});
|
||||
|
||||
final bool isFavorite;
|
||||
final void Function(MobileViewItemBottomSheetBodyAction action) onAction;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
// rename, duplicate
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.m_rename_m,
|
||||
text: LocaleKeys.button_rename.tr(),
|
||||
onTap: () => onAction(
|
||||
MobileViewItemBottomSheetBodyAction.rename,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.m_duplicate_m,
|
||||
text: LocaleKeys.button_duplicate.tr(),
|
||||
onTap: () => onAction(
|
||||
MobileViewItemBottomSheetBodyAction.duplicate,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// share, delete
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.m_share_m,
|
||||
text: LocaleKeys.button_share.tr(),
|
||||
onTap: () => onAction(
|
||||
MobileViewItemBottomSheetBodyAction.share,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.m_delete_m,
|
||||
text: LocaleKeys.button_delete.tr(),
|
||||
onTap: () => onAction(
|
||||
MobileViewItemBottomSheetBodyAction.delete,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// remove from favorites
|
||||
|
||||
BottomSheetActionWidget(
|
||||
svg: isFavorite
|
||||
? FlowySvgs.m_favorite_selected_lg
|
||||
: FlowySvgs.m_favorite_unselected_lg,
|
||||
text: isFavorite
|
||||
? LocaleKeys.button_removeFromFavorites.tr()
|
||||
: LocaleKeys.button_addToFavorites.tr(),
|
||||
onTap: () => onAction(
|
||||
isFavorite
|
||||
? MobileViewItemBottomSheetBodyAction.removeFromFavorites
|
||||
: MobileViewItemBottomSheetBodyAction.addToFavorites,
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MobileViewItemBottomSheetHeader extends StatelessWidget {
|
||||
const MobileViewItemBottomSheetHeader({
|
||||
super.key,
|
||||
required this.view,
|
||||
required this.showBackButton,
|
||||
required this.onBack,
|
||||
});
|
||||
|
||||
final ViewPB view;
|
||||
final bool showBackButton;
|
||||
final VoidCallback onBack;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
// back button,
|
||||
showBackButton
|
||||
? InkWell(
|
||||
onTap: onBack,
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Icon(
|
||||
Icons.arrow_back_ios_new_rounded,
|
||||
size: 24.0,
|
||||
),
|
||||
),
|
||||
)
|
||||
: const HSpace(40.0),
|
||||
// title
|
||||
FlowyText.regular(
|
||||
view.name,
|
||||
fontSize: 16.0,
|
||||
),
|
||||
// placeholder, ensure the title is centered
|
||||
const HSpace(40.0),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
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_action_widget.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_drag_handler.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_rename_widget.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_view_item.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_view_item_header.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum MobileViewBottomSheetBodyAction {
|
||||
undo,
|
||||
redo,
|
||||
share,
|
||||
rename,
|
||||
duplicate,
|
||||
delete,
|
||||
addToFavorites,
|
||||
removeFromFavorites,
|
||||
helpCenter;
|
||||
}
|
||||
|
||||
typedef MobileViewBottomSheetBodyActionCallback = void Function(
|
||||
MobileViewBottomSheetBodyAction action,
|
||||
);
|
||||
|
||||
class ViewPageBottomSheet extends StatefulWidget {
|
||||
const ViewPageBottomSheet({
|
||||
super.key,
|
||||
required this.view,
|
||||
required this.onAction,
|
||||
required this.onRename,
|
||||
});
|
||||
|
||||
final ViewPB view;
|
||||
final MobileViewBottomSheetBodyActionCallback onAction;
|
||||
final void Function(String name) onRename;
|
||||
|
||||
@override
|
||||
State<ViewPageBottomSheet> createState() => _ViewPageBottomSheetState();
|
||||
}
|
||||
|
||||
class _ViewPageBottomSheetState extends State<ViewPageBottomSheet> {
|
||||
MobileBottomSheetType type = MobileBottomSheetType.view;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// drag handler
|
||||
const MobileBottomSheetDragHandler(),
|
||||
|
||||
// header
|
||||
_buildHeader(),
|
||||
const VSpace(8.0),
|
||||
const Divider(),
|
||||
|
||||
// body
|
||||
_buildBody(),
|
||||
const VSpace(24.0),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
switch (type) {
|
||||
case MobileBottomSheetType.view:
|
||||
case MobileBottomSheetType.rename:
|
||||
// header
|
||||
return MobileViewItemBottomSheetHeader(
|
||||
showBackButton: type != MobileBottomSheetType.view,
|
||||
view: widget.view,
|
||||
onBack: () {
|
||||
setState(() {
|
||||
type = MobileBottomSheetType.view;
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildBody() {
|
||||
switch (type) {
|
||||
case MobileBottomSheetType.view:
|
||||
return MobileViewBottomSheetBody(
|
||||
view: widget.view,
|
||||
onAction: (action) {
|
||||
switch (action) {
|
||||
case MobileViewBottomSheetBodyAction.rename:
|
||||
setState(() {
|
||||
type = MobileBottomSheetType.rename;
|
||||
});
|
||||
break;
|
||||
default:
|
||||
widget.onAction(action);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
case MobileBottomSheetType.rename:
|
||||
return MobileBottomSheetRenameWidget(
|
||||
name: widget.view.name,
|
||||
onRename: (name) {
|
||||
widget.onRename(name);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MobileViewBottomSheetBody extends StatelessWidget {
|
||||
const MobileViewBottomSheetBody({
|
||||
super.key,
|
||||
required this.view,
|
||||
required this.onAction,
|
||||
});
|
||||
|
||||
final ViewPB view;
|
||||
final MobileViewBottomSheetBodyActionCallback onAction;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isFavorite = view.isFavorite;
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
// undo, redo
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.m_undo_m,
|
||||
text: LocaleKeys.toolbar_undo.tr(),
|
||||
onTap: () => onAction(
|
||||
MobileViewBottomSheetBodyAction.undo,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.m_redo_m,
|
||||
text: LocaleKeys.toolbar_redo.tr(),
|
||||
onTap: () => onAction(
|
||||
MobileViewBottomSheetBodyAction.redo,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// rename, duplicate
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.m_rename_m,
|
||||
text: LocaleKeys.button_rename.tr(),
|
||||
onTap: () => onAction(
|
||||
MobileViewBottomSheetBodyAction.rename,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.m_duplicate_m,
|
||||
text: LocaleKeys.button_duplicate.tr(),
|
||||
onTap: () => onAction(
|
||||
MobileViewBottomSheetBodyAction.duplicate,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// share, delete
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.m_share_m,
|
||||
text: LocaleKeys.button_share.tr(),
|
||||
onTap: () => onAction(
|
||||
MobileViewBottomSheetBodyAction.share,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.m_delete_m,
|
||||
text: LocaleKeys.button_delete.tr(),
|
||||
onTap: () => onAction(
|
||||
MobileViewBottomSheetBodyAction.delete,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// favorites
|
||||
BottomSheetActionWidget(
|
||||
svg: isFavorite
|
||||
? FlowySvgs.m_favorite_selected_lg
|
||||
: FlowySvgs.m_favorite_unselected_lg,
|
||||
text: isFavorite
|
||||
? LocaleKeys.button_removeFromFavorites.tr()
|
||||
: LocaleKeys.button_addToFavorites.tr(),
|
||||
onTap: () => onAction(
|
||||
isFavorite
|
||||
? MobileViewBottomSheetBodyAction.removeFromFavorites
|
||||
: MobileViewBottomSheetBodyAction.addToFavorites,
|
||||
),
|
||||
),
|
||||
|
||||
// help center
|
||||
BottomSheetActionWidget(
|
||||
svg: FlowySvgs.m_help_center_m,
|
||||
text: LocaleKeys.button_helpCenter.tr(),
|
||||
onTap: () => onAction(
|
||||
MobileViewBottomSheetBodyAction.helpCenter,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.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/view/view_bloc.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
|
||||
enum MobilePaneActionType {
|
||||
delete,
|
||||
addToFavorites,
|
||||
removeFromFavorites,
|
||||
more;
|
||||
|
||||
MobileSlideActionButton actionButton(
|
||||
BuildContext context,
|
||||
) {
|
||||
switch (this) {
|
||||
case MobilePaneActionType.delete:
|
||||
return MobileSlideActionButton(
|
||||
backgroundColor: Colors.red,
|
||||
svg: FlowySvgs.delete_s,
|
||||
size: 30.0,
|
||||
onPressed: (context) =>
|
||||
context.read<ViewBloc>().add(const ViewEvent.delete()),
|
||||
);
|
||||
case MobilePaneActionType.removeFromFavorites:
|
||||
return MobileSlideActionButton(
|
||||
backgroundColor: Colors.orange,
|
||||
svg: FlowySvgs.favorite_s,
|
||||
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,
|
||||
onPressed: (context) => context
|
||||
.read<FavoriteBloc>()
|
||||
.add(FavoriteEvent.toggle(context.read<ViewBloc>().view)),
|
||||
);
|
||||
case MobilePaneActionType.more:
|
||||
return MobileSlideActionButton(
|
||||
backgroundColor: Colors.grey,
|
||||
svg: FlowySvgs.three_dots_vertical_s,
|
||||
size: 28.0,
|
||||
onPressed: (context) {
|
||||
final viewBloc = context.read<ViewBloc>();
|
||||
final favoriteBloc = context.read<FavoriteBloc>();
|
||||
showMobileBottomSheet(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider.value(value: viewBloc),
|
||||
BlocProvider.value(value: favoriteBloc),
|
||||
],
|
||||
child: BlocBuilder<ViewBloc, ViewState>(
|
||||
builder: (context, state) {
|
||||
return MobileViewItemBottomSheet(
|
||||
view: viewBloc.state.view,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActionPane buildEndActionPane(
|
||||
BuildContext context,
|
||||
List<MobilePaneActionType> actions,
|
||||
) {
|
||||
return ActionPane(
|
||||
motion: const ScrollMotion(),
|
||||
extentRatio: actions.length / 5,
|
||||
children: actions
|
||||
.map(
|
||||
(action) => action.actionButton(context),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import 'package:appflowy/mobile/presentation/base/mobile_view_page.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MobileBoardScreen extends StatelessWidget {
|
||||
static const routeName = '/board';
|
||||
static const viewId = 'id';
|
||||
static const viewTitle = 'title';
|
||||
|
||||
const MobileBoardScreen({
|
||||
super.key,
|
||||
required this.id,
|
||||
this.title,
|
||||
});
|
||||
|
||||
/// view id
|
||||
final String id;
|
||||
final String? title;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MobileViewPage(
|
||||
id: id,
|
||||
title: title,
|
||||
viewLayout: ViewLayoutPB.Document,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import 'package:appflowy/mobile/presentation/base/mobile_view_page.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MobileCalendarScreen extends StatelessWidget {
|
||||
static const routeName = '/calendar';
|
||||
static const viewId = 'id';
|
||||
static const viewTitle = 'title';
|
||||
|
||||
const MobileCalendarScreen({
|
||||
super.key,
|
||||
required this.id,
|
||||
this.title,
|
||||
});
|
||||
|
||||
/// view id
|
||||
final String id;
|
||||
final String? title;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MobileViewPage(
|
||||
id: id,
|
||||
title: title,
|
||||
viewLayout: ViewLayoutPB.Document,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import 'package:appflowy/mobile/presentation/base/mobile_view_page.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MobileGridScreen extends StatelessWidget {
|
||||
static const routeName = '/grid';
|
||||
static const viewId = 'id';
|
||||
static const viewTitle = 'title';
|
||||
|
||||
const MobileGridScreen({
|
||||
super.key,
|
||||
required this.id,
|
||||
this.title,
|
||||
});
|
||||
|
||||
/// view id
|
||||
final String id;
|
||||
final String? title;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MobileViewPage(
|
||||
id: id,
|
||||
title: title,
|
||||
viewLayout: ViewLayoutPB.Document,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import 'package:appflowy/mobile/presentation/base/mobile_view_page.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MobileEditorScreen extends StatelessWidget {
|
||||
static const routeName = '/docs';
|
||||
static const viewId = 'id';
|
||||
static const viewTitle = 'title';
|
||||
|
||||
const MobileEditorScreen({
|
||||
super.key,
|
||||
required this.id,
|
||||
this.title,
|
||||
});
|
||||
|
||||
/// view id
|
||||
final String id;
|
||||
final String? title;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MobileViewPage(
|
||||
id: id,
|
||||
title: title,
|
||||
viewLayout: ViewLayoutPB.Document,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MobileErrorPage extends StatelessWidget {
|
||||
const MobileErrorPage({
|
||||
super.key,
|
||||
this.header,
|
||||
this.title,
|
||||
required this.message,
|
||||
});
|
||||
|
||||
final Widget? header;
|
||||
final String? title;
|
||||
final String message;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
header != null
|
||||
? header!
|
||||
: const FlowyText.semibold(
|
||||
'😔',
|
||||
fontSize: 50,
|
||||
),
|
||||
const VSpace(14.0),
|
||||
FlowyText.semibold(
|
||||
title ?? LocaleKeys.error_weAreSorry.tr(),
|
||||
fontSize: 32,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const VSpace(4.0),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: FlowyText.regular(
|
||||
message,
|
||||
fontSize: 16,
|
||||
maxLines: 100,
|
||||
color: Colors.grey, // FIXME: use theme color
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||
import 'package:appflowy/mobile/presentation/error/error_page.dart';
|
||||
import 'package:appflowy/mobile/presentation/home/favorite_folder/mobile_home_favorite_folder.dart';
|
||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/menu/menu_bloc.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.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';
|
||||
|
||||
class MobileFavoritePageFolder extends StatelessWidget {
|
||||
const MobileFavoritePageFolder({
|
||||
super.key,
|
||||
required this.userProfile,
|
||||
required this.workspaceSetting,
|
||||
});
|
||||
|
||||
final UserProfilePB userProfile;
|
||||
final WorkspaceSettingPB workspaceSetting;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
create: (_) => MenuBloc(
|
||||
user: userProfile,
|
||||
workspace: workspaceSetting.workspace,
|
||||
)..add(const MenuEvent.initial()),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (_) => FavoriteBloc()..add(const FavoriteEvent.initial()),
|
||||
)
|
||||
],
|
||||
child: MultiBlocListener(
|
||||
listeners: [
|
||||
BlocListener<MenuBloc, MenuState>(
|
||||
listenWhen: (p, c) =>
|
||||
p.lastCreatedView?.id != c.lastCreatedView?.id,
|
||||
listener: (context, state) =>
|
||||
context.pushView(state.lastCreatedView!),
|
||||
),
|
||||
],
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final favoriteState = context.watch<FavoriteBloc>().state;
|
||||
if (favoriteState.views.isEmpty) {
|
||||
return MobileErrorPage(
|
||||
header: const FlowyText.semibold(
|
||||
'😁',
|
||||
fontSize: 50,
|
||||
),
|
||||
title: LocaleKeys.favorite_noFavorite.tr(),
|
||||
message: LocaleKeys.favorite_noFavoriteHintText.tr(),
|
||||
);
|
||||
}
|
||||
return Scrollbar(
|
||||
child: SingleChildScrollView(
|
||||
child: SlidableAutoCloseBehavior(
|
||||
child: Column(
|
||||
children: [
|
||||
MobileFavoriteFolder(
|
||||
showHeader: false,
|
||||
forceExpanded: true,
|
||||
views: favoriteState.views,
|
||||
),
|
||||
const VSpace(100.0),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
import 'package:appflowy/mobile/presentation/favorite/mobile_favorite_folder.dart';
|
||||
import 'package:appflowy/mobile/presentation/home/mobile_home_page_header.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/errors/workspace_failed_screen.dart';
|
||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/workspace.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MobileFavoriteScreen extends StatelessWidget {
|
||||
const MobileFavoriteScreen({
|
||||
super.key,
|
||||
});
|
||||
|
||||
static const routeName = '/favorite';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: Future.wait([
|
||||
FolderEventGetCurrentWorkspace().send(),
|
||||
getIt<AuthService>().getUser(),
|
||||
]),
|
||||
builder: (context, snapshots) {
|
||||
if (!snapshots.hasData) {
|
||||
return const Center(child: CircularProgressIndicator.adaptive());
|
||||
}
|
||||
|
||||
final workspaceSetting = snapshots.data?[0].fold(
|
||||
(workspaceSettingPB) {
|
||||
return workspaceSettingPB as WorkspaceSettingPB?;
|
||||
},
|
||||
(error) => null,
|
||||
);
|
||||
final userProfile =
|
||||
snapshots.data?[1].fold((error) => null, (userProfilePB) {
|
||||
return userProfilePB as UserProfilePB?;
|
||||
});
|
||||
|
||||
// In the unlikely case either of the above is null, eg.
|
||||
// when a workspace is already open this can happen.
|
||||
if (workspaceSetting == null || userProfile == null) {
|
||||
return const WorkspaceFailedScreen();
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
child: MobileFavoritePage(
|
||||
userProfile: userProfile,
|
||||
workspaceSetting: workspaceSetting,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MobileFavoritePage extends StatelessWidget {
|
||||
const MobileFavoritePage({
|
||||
super.key,
|
||||
required this.userProfile,
|
||||
required this.workspaceSetting,
|
||||
});
|
||||
|
||||
final UserProfilePB userProfile;
|
||||
final WorkspaceSettingPB workspaceSetting;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
// Header
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: MobileHomePageHeader(
|
||||
userProfile: userProfile,
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
|
||||
// Folder
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: MobileFavoritePageFolder(
|
||||
userProfile: userProfile,
|
||||
workspaceSetting: workspaceSetting,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
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';
|
||||
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-folder2/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 {
|
||||
const MobileFavoriteFolder({
|
||||
super.key,
|
||||
required this.views,
|
||||
this.showHeader = true,
|
||||
this.forceExpanded = false,
|
||||
});
|
||||
|
||||
final bool showHeader;
|
||||
final bool forceExpanded;
|
||||
final List<ViewPB> views;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (views.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return BlocProvider<FolderBloc>(
|
||||
create: (context) => FolderBloc(type: FolderCategoryType.favorite)
|
||||
..add(
|
||||
const FolderEvent.initial(),
|
||||
),
|
||||
child: BlocBuilder<FolderBloc, FolderState>(
|
||||
builder: (context, state) {
|
||||
return Column(
|
||||
children: [
|
||||
if (showHeader) ...[
|
||||
MobileFavoriteFolderHeader(
|
||||
isExpanded: context.read<FolderBloc>().state.isExpanded,
|
||||
onPressed: () => context
|
||||
.read<FolderBloc>()
|
||||
.add(const FolderEvent.expandOrUnExpand()),
|
||||
onAdded: () => context.read<FolderBloc>().add(
|
||||
const FolderEvent.expandOrUnExpand(isExpanded: true),
|
||||
),
|
||||
),
|
||||
const VSpace(8.0),
|
||||
const Divider(
|
||||
height: 1,
|
||||
),
|
||||
],
|
||||
if (forceExpanded || state.isExpanded)
|
||||
...views.map(
|
||||
(view) => MobileViewItem(
|
||||
key: ValueKey(
|
||||
'${FolderCategoryType.favorite.name} ${view.id}',
|
||||
),
|
||||
categoryType: FolderCategoryType.favorite,
|
||||
isDraggable: false,
|
||||
isFirstChild: view.id == views.first.id,
|
||||
isFeedback: false,
|
||||
view: view,
|
||||
level: 0,
|
||||
onSelected: (view) async {
|
||||
await context.pushView(view);
|
||||
},
|
||||
endActionPane: (context) => buildEndActionPane(context, [
|
||||
view.isFavorite
|
||||
? MobilePaneActionType.removeFromFavorites
|
||||
: MobilePaneActionType.addToFavorites,
|
||||
MobilePaneActionType.more,
|
||||
]),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MobileFavoriteFolderHeader extends StatefulWidget {
|
||||
const MobileFavoriteFolderHeader({
|
||||
super.key,
|
||||
required this.onPressed,
|
||||
required this.onAdded,
|
||||
required this.isExpanded,
|
||||
});
|
||||
|
||||
final VoidCallback onPressed;
|
||||
final VoidCallback onAdded;
|
||||
final bool isExpanded;
|
||||
|
||||
@override
|
||||
State<MobileFavoriteFolderHeader> createState() =>
|
||||
_MobileFavoriteFolderHeaderState();
|
||||
}
|
||||
|
||||
class _MobileFavoriteFolderHeaderState
|
||||
extends State<MobileFavoriteFolderHeader> {
|
||||
double _turns = 0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: FlowyButton(
|
||||
text: FlowyText.semibold(
|
||||
LocaleKeys.sideBar_favorites.tr(),
|
||||
fontSize: 20.0,
|
||||
),
|
||||
margin: const EdgeInsets.symmetric(vertical: 8),
|
||||
expandText: false,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
rightIcon: AnimatedRotation(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
turns: _turns,
|
||||
child: const Icon(
|
||||
Icons.keyboard_arrow_down_rounded,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_turns = widget.isExpanded ? -0.25 : 0;
|
||||
});
|
||||
widget.onPressed();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||