Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3454af1caa | ||
|
|
8d50ad2936 | ||
|
|
daedf95c95 | ||
|
|
95bfda9dc6 | ||
|
|
93664167e0 | ||
|
|
dfe3b0a929 | ||
|
|
e2028ac5a0 | ||
|
|
c9dc24a13c | ||
|
|
609557c357 | ||
|
|
93af6e69a1 | ||
|
|
b75947b630 | ||
|
|
b60d25a0e3 | ||
|
|
537fa52e77 | ||
|
|
d861ce6227 | ||
|
|
c3e5aa29fc | ||
|
|
4ca3ba8e08 | ||
|
|
f5cc6521fb | ||
|
|
cea1c17b76 | ||
|
|
2abb396467 | ||
|
|
236b5bfe90 | ||
|
|
fa3bc97698 | ||
|
|
c0796e8ae5 | ||
|
|
acd75befbc | ||
|
|
746f0817bc | ||
|
|
c0b667b4ea | ||
|
|
ef83688907 | ||
|
|
c06a0346d8 | ||
|
|
4a0c48f419 | ||
|
|
7802c75d7c | ||
|
|
f1831d07af | ||
|
|
6636731487 | ||
|
|
a4a2a4088b | ||
|
|
c500cd1287 | ||
|
|
6f173c2ada | ||
|
|
34fb1bcfa4 | ||
|
|
58aa73b5be | ||
|
|
334aedd6c6 | ||
|
|
a6c6aa819c | ||
|
|
15c9a67028 | ||
|
|
42cb032bca | ||
|
|
a3a5709b70 | ||
|
|
3a4247c304 | ||
|
|
8eaadccda0 | ||
|
|
2b6b10788d | ||
|
|
26f8bbf7c6 | ||
|
|
252699d249 | ||
|
|
b2e55c4e2e | ||
|
|
d5ee372131 | ||
|
|
39588a4731 | ||
|
|
211559ed6e | ||
|
|
a637517413 | ||
|
|
ddbe0fa698 | ||
|
|
ee61ef3f12 | ||
|
|
54bfc65664 | ||
|
|
c159a5e42b | ||
|
|
1311e2d379 | ||
|
|
e6aa57275a | ||
|
|
d690ca2751 | ||
|
|
3e0a003872 | ||
|
|
e81a2ff577 |
No files matched your search
@@ -9,7 +9,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# get build name from pubspec.yaml
|
||||
- name: Get build version
|
||||
@@ -19,7 +19,6 @@ jobs:
|
||||
echo "fetching version from pubspec.yaml..."
|
||||
echo "build_name=$(grep 'version: ' pubspec.yaml | awk '{print $2}')" >> $GITHUB_OUTPUT
|
||||
|
||||
|
||||
- uses: peter-evans/slash-command-dispatch@v4
|
||||
with:
|
||||
token: ${{ secrets.PAT }}
|
||||
|
||||
@@ -5,8 +5,7 @@ jobs:
|
||||
commitlint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: wagoid/commitlint-github-action@v4
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build the app
|
||||
shell: bash
|
||||
|
||||
@@ -9,6 +9,7 @@ on:
|
||||
- ".github/workflows/flutter_ci.yaml"
|
||||
- "frontend/rust-lib/**"
|
||||
- "frontend/appflowy_flutter/**"
|
||||
- "frontend/resources/**"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
@@ -18,10 +19,11 @@ on:
|
||||
- ".github/workflows/flutter_ci.yaml"
|
||||
- "frontend/rust-lib/**"
|
||||
- "frontend/appflowy_flutter/**"
|
||||
- "frontend/resources/**"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
FLUTTER_VERSION: "3.18.0-0.2.pre"
|
||||
FLUTTER_VERSION: "3.19.0"
|
||||
RUST_TOOLCHAIN: "1.75"
|
||||
CARGO_MAKE_VERSION: "0.36.6"
|
||||
|
||||
@@ -59,7 +61,7 @@ jobs:
|
||||
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
|
||||
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
@@ -74,7 +76,7 @@ jobs:
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "beta"
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -120,7 +122,7 @@ jobs:
|
||||
run: |
|
||||
tar -czf appflowy_flutter.tar.gz frontend/appflowy_flutter
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ github.run_id }}-${{ matrix.os }}
|
||||
path: appflowy_flutter.tar.gz
|
||||
@@ -140,7 +142,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
@@ -155,7 +157,7 @@ jobs:
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "beta"
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -192,7 +194,7 @@ jobs:
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ github.run_id }}-${{ matrix.os }}
|
||||
|
||||
@@ -216,11 +218,11 @@ jobs:
|
||||
shell: bash
|
||||
|
||||
cloud_integration_test:
|
||||
needs: [ prepare ]
|
||||
needs: [prepare]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ ubuntu-latest ]
|
||||
os: [ubuntu-latest]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
flutter_profile: development-linux-x86_64
|
||||
@@ -229,11 +231,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout appflowy cloud code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: AppFlowy-IO/AppFlowy-Cloud
|
||||
path: AppFlowy-Cloud
|
||||
depth: 1
|
||||
|
||||
- name: Prepare appflowy cloud env
|
||||
working-directory: AppFlowy-Cloud
|
||||
@@ -253,13 +254,13 @@ jobs:
|
||||
sleep 10
|
||||
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install flutter
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "beta"
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
|
||||
- uses: taiki-e/install-action@v2
|
||||
@@ -280,7 +281,7 @@ jobs:
|
||||
flutter config --enable-linux-desktop
|
||||
shell: bash
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ github.run_id }}-${{ matrix.os }}
|
||||
|
||||
@@ -318,7 +319,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
@@ -333,7 +334,7 @@ jobs:
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "beta"
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
|
||||
- uses: taiki-e/install-action@v2
|
||||
@@ -363,7 +364,7 @@ jobs:
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ github.run_id }}-${{ matrix.os }}
|
||||
|
||||
@@ -409,7 +410,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
@@ -424,7 +425,7 @@ jobs:
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "beta"
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
|
||||
- uses: taiki-e/install-action@v2
|
||||
@@ -454,7 +455,7 @@ jobs:
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ github.run_id }}-${{ matrix.os }}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ on:
|
||||
- "!frontend/appflowy_tauri/**"
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.18.0-0.2.pre"
|
||||
FLUTTER_VERSION: "3.19.0"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
@@ -51,13 +51,13 @@ jobs:
|
||||
sudo rm -rf $ANDROID_HOME/ndk
|
||||
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install flutter
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "beta"
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
ndk-version: "r24"
|
||||
add-to-path: true
|
||||
|
||||
- uses: gradle/gradle-build-action@v2
|
||||
- uses: gradle/gradle-build-action@v3
|
||||
with:
|
||||
gradle-version: 7.6.3
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ on:
|
||||
- "*"
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.18.0-0.2.pre"
|
||||
FLUTTER_VERSION: "3.19.0"
|
||||
RUST_TOOLCHAIN: "1.75"
|
||||
|
||||
jobs:
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build release notes
|
||||
run: |
|
||||
@@ -52,12 +52,12 @@ jobs:
|
||||
- { target: x86_64-pc-windows-msvc, os: windows-2019 }
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "beta"
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
|
||||
- name: Install Rust toolchain
|
||||
@@ -138,12 +138,12 @@ jobs:
|
||||
- { target: x86_64-apple-darwin, os: macos-11, extra-build-args: "" }
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "beta"
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
|
||||
- name: Install Rust toolchain
|
||||
@@ -238,12 +238,12 @@ jobs:
|
||||
}
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "beta"
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
|
||||
- name: Install Rust toolchain
|
||||
@@ -343,12 +343,12 @@ jobs:
|
||||
}
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "beta"
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
|
||||
- name: Install Rust toolchain
|
||||
@@ -455,19 +455,19 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./frontend/scripts/docker-buildfiles/Dockerfile
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
@@ -45,16 +45,15 @@ jobs:
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: 'ubuntu-latest'
|
||||
prefix-key: "ubuntu-latest"
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
|
||||
- name: Checkout appflowy cloud code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: AppFlowy-IO/AppFlowy-Cloud
|
||||
path: AppFlowy-Cloud
|
||||
depth: 1
|
||||
|
||||
- name: Prepare appflowy cloud env
|
||||
working-directory: AppFlowy-Cloud
|
||||
@@ -80,7 +79,6 @@ jobs:
|
||||
af_cloud_test_gotrue_url: http://localhost/gotrue
|
||||
run: cargo test --no-default-features --features="rev-sqlite,dart" -- --nocapture
|
||||
|
||||
|
||||
- name: rustfmt rust-lib
|
||||
run: cargo fmt --all -- --check
|
||||
working-directory: frontend/rust-lib/
|
||||
|
||||
@@ -10,7 +10,7 @@ on:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
FLUTTER_VERSION: "3.18.0-0.2.pre"
|
||||
FLUTTER_VERSION: "3.19.0"
|
||||
RUST_TOOLCHAIN: "1.75"
|
||||
|
||||
jobs:
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "beta"
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ on:
|
||||
- ".github/workflows/tauri_ci.yaml"
|
||||
- "frontend/rust-lib/**"
|
||||
- "frontend/appflowy_tauri/**"
|
||||
- "frontend/resources/**"
|
||||
|
||||
env:
|
||||
NODE_VERSION: "18.16.0"
|
||||
@@ -21,8 +22,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# platform: [macos-latest, ubuntu-latest, windows-latest]
|
||||
platform: [ubuntu-latest]
|
||||
platform: [ubuntu-latest]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
|
||||
@@ -26,9 +26,9 @@ jobs:
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
@@ -40,14 +40,16 @@ jobs:
|
||||
frontend/rust-lib
|
||||
frontend/appflowy_web/appflowy_wasm
|
||||
|
||||
# TODO: Can combine caching deps and node_modules in one
|
||||
# See Glob patterns: https://github.com/actions/toolkit/tree/main/packages/glob
|
||||
- name: Cache Node.js dependencies
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: npm-${{ runner.os }}
|
||||
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: frontend/appflowy_web/node_modules
|
||||
key: node-modules-${{ runner.os }}
|
||||
|
||||
+24
-1
@@ -1,5 +1,28 @@
|
||||
# Release Notes
|
||||
## Version 0.4.7 - 02/08/2024
|
||||
## Version 0.5.0 - 02/26/2024
|
||||
### New Features
|
||||
- Added support for scaling text on mobile platforms for better readability.
|
||||
- Introduced a toggle for favorites directly from the documents' top bar.
|
||||
- Optimized the image upload process and added error messaging for failed uploads。
|
||||
- Implemented depth control for outline block components.
|
||||
- New checklist task creation is now more intuitive, with prompts appearing on hover over list items in the row detail page.
|
||||
- Enhanced sorting capabilities, allowing reordering and addition of multiple sorts.
|
||||
- Expanded sorting and filtering options to include more field types like checklist, creation time, and modification time.
|
||||
- Added support for field calculations within databases.
|
||||
### Bug Fixes
|
||||
- Fixed an issue where inserting an image from Unsplash in local mode was not possible.
|
||||
- Fixed undo/redo functionality in lists.
|
||||
- Fixed data loss issues when converting between block types.
|
||||
- Fixed a bug where newly created rows were not being automatically sorted.
|
||||
- Fixed issues related to deleting a sorting field or sort not removing existing sorts properly.
|
||||
### Notes
|
||||
- Windows 7, Windows 8, and iOS 11 are not yet supported due to the upgrade to Flutter 3.19.0.
|
||||
|
||||
## Version 0.4.9 - 02/17/2024
|
||||
### Bug Fixes
|
||||
- Resolved the issue that caused users to be redirected to the Sign In page
|
||||
|
||||
## Version 0.4.8 - 02/13/2024
|
||||
### Bug Fixes
|
||||
- Fixed a possible error when loading workspaces
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
|
||||
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
|
||||
CARGO_MAKE_CRATE_NAME = "dart-ffi"
|
||||
LIB_NAME = "dart_ffi"
|
||||
APPFLOWY_VERSION = "0.4.7"
|
||||
APPFLOWY_VERSION = "0.5.0"
|
||||
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite"
|
||||
PRODUCT_NAME = "AppFlowy"
|
||||
MACOSX_DEPLOYMENT_TARGET = "11.0"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int64_t init_sdk(char *data);
|
||||
int64_t init_sdk(int64_t port, char *data);
|
||||
|
||||
void async_event(int64_t port, const uint8_t *input, uintptr_t len);
|
||||
|
||||
@@ -13,6 +13,6 @@ int32_t set_stream_port(int64_t port);
|
||||
|
||||
void link_me_please(void);
|
||||
|
||||
void backend_log(int64_t level, const char *data);
|
||||
void rust_log(int64_t level, const char *data);
|
||||
|
||||
void set_env(const char *data);
|
||||
@@ -9,7 +9,7 @@ import '../util/database_test_op.dart';
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('database filter', () {
|
||||
group('grid filter:', () {
|
||||
testWidgets('add text filter', (tester) async {
|
||||
await tester.openV020database();
|
||||
|
||||
|
||||
+109
-9
@@ -1,11 +1,18 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/outline/outline_block_component.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../util/util.dart';
|
||||
|
||||
const String heading1 = "Heading 1";
|
||||
const String heading2 = "Heading 2";
|
||||
const String heading3 = "Heading 3";
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
@@ -33,12 +40,8 @@ void main() {
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'outline_test',
|
||||
);
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
|
||||
await tester.ime.insertText('# Heading 1\n');
|
||||
await tester.ime.insertText('## Heading 2\n');
|
||||
await tester.ime.insertText('### Heading 3\n');
|
||||
|
||||
await insertHeadingComponent(tester);
|
||||
/* Results in:
|
||||
* # Heading 1
|
||||
* ## Heading 2
|
||||
@@ -51,7 +54,7 @@ void main() {
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(OutlineBlockWidget),
|
||||
matching: find.text('Heading 1'),
|
||||
matching: find.text(heading1),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
@@ -60,7 +63,7 @@ void main() {
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(OutlineBlockWidget),
|
||||
matching: find.text('Heading 2'),
|
||||
matching: find.text(heading2),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
@@ -69,7 +72,7 @@ void main() {
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(OutlineBlockWidget),
|
||||
matching: find.text('Heading 3'),
|
||||
matching: find.text(heading3),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
@@ -80,11 +83,86 @@ void main() {
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(OutlineBlockWidget),
|
||||
matching: find.text('Heading 1Hello world'),
|
||||
matching: find.text('${heading1}Hello world'),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets("control the depth of outline block", (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'outline_test',
|
||||
);
|
||||
|
||||
await insertHeadingComponent(tester);
|
||||
/* Results in:
|
||||
* # Heading 1
|
||||
* ## Heading 2
|
||||
* ### Heading 3
|
||||
*/
|
||||
|
||||
await tester.editor.tapLineOfEditorAt(3);
|
||||
await insertOutlineInDocument(tester);
|
||||
|
||||
// expect to find only the `heading1` widget under the [OutlineBlockWidget]
|
||||
await hoverAndClickDepthOptionAction(tester, [3], 1);
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(OutlineBlockWidget),
|
||||
matching: find.text(heading2),
|
||||
),
|
||||
findsNothing,
|
||||
);
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(OutlineBlockWidget),
|
||||
matching: find.text(heading3),
|
||||
),
|
||||
findsNothing,
|
||||
);
|
||||
//////
|
||||
|
||||
/// expect to find only the 'heading1' and 'heading2' under the [OutlineBlockWidget]
|
||||
await hoverAndClickDepthOptionAction(tester, [3], 2);
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(OutlineBlockWidget),
|
||||
matching: find.text(heading3),
|
||||
),
|
||||
findsNothing,
|
||||
);
|
||||
//////
|
||||
|
||||
// expect to find all the headings under the [OutlineBlockWidget]
|
||||
await hoverAndClickDepthOptionAction(tester, [3], 3);
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(OutlineBlockWidget),
|
||||
matching: find.text(heading1),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(OutlineBlockWidget),
|
||||
matching: find.text(heading2),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(OutlineBlockWidget),
|
||||
matching: find.text(heading3),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
//////
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -97,3 +175,25 @@ Future<void> insertOutlineInDocument(WidgetTester tester) async {
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
Future<void> hoverAndClickDepthOptionAction(
|
||||
WidgetTester tester,
|
||||
List<int> path,
|
||||
int level,
|
||||
) async {
|
||||
await tester.editor.hoverAndClickOptionMenuButton([3]);
|
||||
await tester.tap(find.byType(AppFlowyPopover).hitTestable().last);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Find a total of 4 HoverButtons under the [BlockOptionButton],
|
||||
// in addition to 3 HoverButtons under the [DepthOptionAction] - (child of BlockOptionButton)
|
||||
await tester.tap(find.byType(HoverButton).hitTestable().at(3 + level));
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
Future<void> insertHeadingComponent(WidgetTester tester) async {
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.ime.insertText('# $heading1\n');
|
||||
await tester.ime.insertText('## $heading2\n');
|
||||
await tester.ime.insertText('### $heading3\n');
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import 'dart:io';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/emoji_picker/emoji_picker.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_editor/src/editor/editor_component/service/editor.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/keyboard.dart';
|
||||
import 'util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
// May be better to move this to an existing test but unsure what it fits with
|
||||
group('Keyboard shortcuts related to emojis', () {
|
||||
testWidgets('cmd/ctrl+alt+e shortcut opens the emoji picker',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
final Finder editor = find.byType(AppFlowyEditor);
|
||||
await tester.tap(editor);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(EmojiSelectionMenu), findsNothing);
|
||||
|
||||
await FlowyTestKeyboard.simulateKeyDownEvent(
|
||||
[
|
||||
Platform.isMacOS
|
||||
? LogicalKeyboardKey.meta
|
||||
: LogicalKeyboardKey.control,
|
||||
LogicalKeyboardKey.alt,
|
||||
LogicalKeyboardKey.keyE,
|
||||
],
|
||||
tester: tester,
|
||||
);
|
||||
|
||||
expect(find.byType(EmojiSelectionMenu), findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -23,6 +23,7 @@ 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;
|
||||
import 'emoji_shortcut_test.dart' as emoji_shortcut_test;
|
||||
// import 'auth/supabase_auth_test.dart' as supabase_auth_test_runner;
|
||||
|
||||
/// The main task runner for all integration tests in AppFlowy.
|
||||
@@ -69,6 +70,7 @@ Future<void> main() async {
|
||||
|
||||
// Others
|
||||
hotkeys_test.main();
|
||||
emoji_shortcut_test.main();
|
||||
|
||||
// Appearance integration test
|
||||
appearance_test_runner.main();
|
||||
|
||||
@@ -306,7 +306,7 @@ extension CommonOperations on WidgetTester {
|
||||
KVKeys.showRenameDialogWhenCreatingNewFile,
|
||||
(value) => bool.parse(value),
|
||||
);
|
||||
final showRenameDialog = settingsOrFailure.fold(() => false, (r) => r);
|
||||
final showRenameDialog = settingsOrFailure ?? false;
|
||||
if (showRenameDialog) {
|
||||
await tapOKButton();
|
||||
}
|
||||
|
||||
@@ -1116,7 +1116,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
|
||||
/// Must call [tapSortMenuInSettingBar] first.
|
||||
Future<void> tapAllSortButton() async {
|
||||
await tapButton(find.byType(DatabaseDeleteSortButton));
|
||||
await tapButton(find.byType(DeleteAllSortsButton));
|
||||
}
|
||||
|
||||
Future<void> scrollOptionFilterListByOffset(Offset offset) async {
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:appflowy/plugins/base/emoji/emoji_picker.dart';
|
||||
import 'package:appflowy/plugins/base/emoji/emoji_skin_tone.dart';
|
||||
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_add_button.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_option_button.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/cover_editor.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/document_header_node_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart';
|
||||
@@ -241,4 +242,25 @@ class EditorOperations {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// hover and click on the option menu button beside the block component.
|
||||
Future<void> hoverAndClickOptionMenuButton(Path path) async {
|
||||
final optionMenuButton = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is BlockComponentActionWrapper &&
|
||||
widget.node.path.equals(path),
|
||||
);
|
||||
await tester.hoverOnWidget(
|
||||
optionMenuButton,
|
||||
onHover: () async {
|
||||
await tester.tapButton(
|
||||
find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is BlockOptionButton &&
|
||||
widget.blockComponentContext.node.path.equals(path),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,6 @@
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>11.0</string>
|
||||
<string>12.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,5 +1,5 @@
|
||||
# Uncomment this line to define a global platform for your project
|
||||
# platform :ios, '11.0'
|
||||
# platform :ios, '12.0'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
|
||||
@@ -169,8 +169,8 @@ SPEC CHECKSUMS:
|
||||
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
|
||||
file_picker: 15fd9539e4eb735dc54bae8c0534a7a9511a03de
|
||||
flowy_infra_ui: 0455e1fa8c51885aa1437848e361e99419f34ebc
|
||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||
fluttertoast: fafc4fa4d01a6a9e4f772ecd190ffa525e9e2d9c
|
||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||
fluttertoast: 31b00dabfa7fb7bacd9e7dbee580d7a2ff4bf265
|
||||
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
|
||||
image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425
|
||||
integration_test: 13825b8a9334a850581300559b8839134b124670
|
||||
@@ -189,6 +189,6 @@ SPEC CHECKSUMS:
|
||||
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
|
||||
url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812
|
||||
|
||||
PODFILE CHECKSUM: 8c681999c7764593c94846b2a64b44d86f7a27ac
|
||||
PODFILE CHECKSUM: d94f9be27d1db182e9bc77d10f065555d518f127
|
||||
|
||||
COCOAPODS: 1.12.1
|
||||
COCOAPODS: 1.15.2
|
||||
@@ -143,7 +143,7 @@
|
||||
97C146E61CF9000F007C117D /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1430;
|
||||
LastUpgradeCheck = 1510;
|
||||
ORGANIZATIONNAME = "";
|
||||
TargetAttributes = {
|
||||
97C146ED1CF9000F007C117D = {
|
||||
@@ -330,7 +330,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
@@ -419,7 +419,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
@@ -468,7 +468,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1430"
|
||||
LastUpgradeVersion = "1510"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
abstract class KeyValueStorage {
|
||||
Future<void> set(String key, String value);
|
||||
Future<Option<String>> get(String key);
|
||||
Future<Option<T>> getWithFormat<T>(
|
||||
Future<String?> get(String key);
|
||||
Future<T?> getWithFormat<T>(
|
||||
String key,
|
||||
T Function(String value) formatter,
|
||||
);
|
||||
@@ -17,26 +16,26 @@ class DartKeyValue implements KeyValueStorage {
|
||||
SharedPreferences get sharedPreferences => _sharedPreferences!;
|
||||
|
||||
@override
|
||||
Future<Option<String>> get(String key) async {
|
||||
Future<String?> get(String key) async {
|
||||
await _initSharedPreferencesIfNeeded();
|
||||
|
||||
final value = sharedPreferences.getString(key);
|
||||
if (value != null) {
|
||||
return Some(value);
|
||||
return value;
|
||||
}
|
||||
return none();
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Option<T>> getWithFormat<T>(
|
||||
Future<T?> getWithFormat<T>(
|
||||
String key,
|
||||
T Function(String value) formatter,
|
||||
) async {
|
||||
final value = await get(key);
|
||||
return value.fold(
|
||||
() => none(),
|
||||
(s) => Some(formatter(s)),
|
||||
);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return formatter(value);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -49,6 +49,13 @@ class KVKeys {
|
||||
|
||||
static const String kCloudType = 'kCloudType';
|
||||
static const String kAppflowyCloudBaseURL = 'kAppFlowyCloudBaseURL';
|
||||
static const String kSupabaseURL = 'kSupbaseURL';
|
||||
static const String kSupabaseURL = 'kSupabaseURL';
|
||||
static const String kSupabaseAnonKey = 'kSupabaseAnonKey';
|
||||
|
||||
/// The key for saving the text scale factor.
|
||||
///
|
||||
/// The value is a double string.
|
||||
/// The value range is from 0.8 to 1.0. If it's greater than 1.0, it will cause
|
||||
/// the text to be too large and not aligned with the icon
|
||||
static const String textScaleFactor = 'textScaleFactor';
|
||||
}
|
||||
@@ -1,2 +1 @@
|
||||
export 'target_platform.dart';
|
||||
export 'url_validator.dart';
|
||||
@@ -1,21 +0,0 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'url_validator.freezed.dart';
|
||||
|
||||
Either<UriFailure, Uri> parseValidUrl(String url) {
|
||||
try {
|
||||
final uri = Uri.parse(url);
|
||||
if (uri.scheme.isEmpty || uri.host.isEmpty) {
|
||||
return left(const UriFailure.invalidSchemeHost());
|
||||
}
|
||||
return right(uri);
|
||||
} on FormatException {
|
||||
return left(const UriFailure.invalidUriFormat());
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class UriFailure with _$UriFailure {
|
||||
const factory UriFailure.invalidSchemeHost() = _InvalidSchemeHost;
|
||||
const factory UriFailure.invalidUriFormat() = _InvalidUriFormat;
|
||||
}
|
||||
@@ -3,11 +3,11 @@ import 'dart:typed_data';
|
||||
import 'package:appflowy/core/notification/notification_helper.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-document/notification.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
|
||||
typedef DocumentNotificationCallback = void Function(
|
||||
DocumentNotification,
|
||||
Either<Uint8List, FlowyError>,
|
||||
FlowyResult<Uint8List, FlowyError>,
|
||||
);
|
||||
|
||||
class DocumentNotificationParser
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:appflowy_backend/protobuf/flowy-notification/protobuf.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/notification.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-notification/protobuf.dart';
|
||||
import 'package:appflowy_backend/rust_stream.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
|
||||
import 'notification_helper.dart';
|
||||
|
||||
// Folder
|
||||
typedef FolderNotificationCallback = void Function(
|
||||
FolderNotification,
|
||||
Either<Uint8List, FlowyError>,
|
||||
FlowyResult<Uint8List, FlowyError>,
|
||||
);
|
||||
|
||||
class FolderNotificationParser
|
||||
@@ -27,7 +28,7 @@ class FolderNotificationParser
|
||||
|
||||
typedef FolderNotificationHandler = Function(
|
||||
FolderNotification ty,
|
||||
Either<Uint8List, FlowyError> result,
|
||||
FlowyResult<Uint8List, FlowyError> result,
|
||||
);
|
||||
|
||||
class FolderNotificationListener {
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:appflowy_backend/protobuf/flowy-notification/protobuf.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/notification.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-notification/protobuf.dart';
|
||||
import 'package:appflowy_backend/rust_stream.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
|
||||
import 'notification_helper.dart';
|
||||
|
||||
// DatabasePB
|
||||
typedef DatabaseNotificationCallback = void Function(
|
||||
DatabaseNotification,
|
||||
Either<Uint8List, FlowyError>,
|
||||
FlowyResult<Uint8List, FlowyError>,
|
||||
);
|
||||
|
||||
class DatabaseNotificationParser
|
||||
@@ -27,7 +28,7 @@ class DatabaseNotificationParser
|
||||
|
||||
typedef DatabaseNotificationHandler = Function(
|
||||
DatabaseNotification ty,
|
||||
Either<Uint8List, FlowyError> result,
|
||||
FlowyResult<Uint8List, FlowyError> result,
|
||||
);
|
||||
|
||||
class DatabaseNotificationListener {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:appflowy_backend/protobuf/flowy-notification/protobuf.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
|
||||
class NotificationParser<T, E> {
|
||||
NotificationParser({
|
||||
@@ -11,7 +12,7 @@ class NotificationParser<T, E> {
|
||||
});
|
||||
|
||||
String? id;
|
||||
void Function(T, Either<Uint8List, E>) callback;
|
||||
void Function(T, FlowyResult<Uint8List, E>) callback;
|
||||
E Function(Uint8List) errorParser;
|
||||
T? Function(int) tyParser;
|
||||
|
||||
@@ -30,10 +31,10 @@ class NotificationParser<T, E> {
|
||||
if (subject.hasError()) {
|
||||
final bytes = Uint8List.fromList(subject.error);
|
||||
final error = errorParser(bytes);
|
||||
callback(ty, right(error));
|
||||
callback(ty, FlowyResult.failure(error));
|
||||
} else {
|
||||
final bytes = Uint8List.fromList(subject.payload);
|
||||
callback(ty, left(bytes));
|
||||
callback(ty, FlowyResult.success(bytes));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,18 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-notification/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/rust_stream.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
|
||||
import 'notification_helper.dart';
|
||||
|
||||
// User
|
||||
typedef UserNotificationCallback = void Function(
|
||||
UserNotification,
|
||||
Either<Uint8List, FlowyError>,
|
||||
FlowyResult<Uint8List, FlowyError>,
|
||||
);
|
||||
|
||||
class UserNotificationParser
|
||||
@@ -27,7 +28,7 @@ class UserNotificationParser
|
||||
|
||||
typedef UserNotificationHandler = Function(
|
||||
UserNotification ty,
|
||||
Either<Uint8List, FlowyError> result,
|
||||
FlowyResult<Uint8List, FlowyError> result,
|
||||
);
|
||||
|
||||
class UserNotificationListener {
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
extension RawKeyboardExtension on RawKeyboard {
|
||||
bool get isAltPressed => keysPressed.any(
|
||||
(key) => [
|
||||
LogicalKeyboardKey.alt,
|
||||
LogicalKeyboardKey.altLeft,
|
||||
LogicalKeyboardKey.altRight,
|
||||
].contains(key),
|
||||
);
|
||||
|
||||
bool get isControlPressed => keysPressed.any(
|
||||
(key) => [
|
||||
LogicalKeyboardKey.control,
|
||||
LogicalKeyboardKey.controlLeft,
|
||||
LogicalKeyboardKey.controlRight,
|
||||
].contains(key),
|
||||
);
|
||||
}
|
||||
@@ -1,19 +1,25 @@
|
||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-date/entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
|
||||
class DateService {
|
||||
static Future<Either<FlowyError, DateTime>> queryDate(String search) async {
|
||||
static Future<FlowyResult<DateTime, FlowyError>> queryDate(
|
||||
String search,
|
||||
) async {
|
||||
final query = DateQueryPB.create()..query = search;
|
||||
final result = (await DateEventQueryDate(query).send()).swap();
|
||||
return result.fold((l) => left(l), (r) {
|
||||
final date = DateTime.tryParse(r.date);
|
||||
if (date != null) {
|
||||
return right(date);
|
||||
}
|
||||
|
||||
return left(FlowyError(msg: 'Could not parse Date (NLP) from String'));
|
||||
});
|
||||
final result = await DateEventQueryDate(query).send();
|
||||
return result.fold(
|
||||
(s) {
|
||||
final date = DateTime.tryParse(s.date);
|
||||
if (date != null) {
|
||||
return FlowyResult.success(date);
|
||||
}
|
||||
return FlowyResult.failure(
|
||||
FlowyError(msg: 'Could not parse Date (NLP) from String'),
|
||||
);
|
||||
},
|
||||
(e) => FlowyResult.failure(e),
|
||||
);
|
||||
}
|
||||
}
|
||||
+27
-37
@@ -4,7 +4,6 @@ import 'package:appflowy/env/backend_env.dart';
|
||||
import 'package:appflowy/env/env.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
/// Sets the cloud type for the application.
|
||||
///
|
||||
@@ -52,7 +51,7 @@ const String kAppflowyCloudUrl = "https://beta.appflowy.cloud";
|
||||
///
|
||||
Future<AuthenticatorType> getAuthenticatorType() async {
|
||||
final value = await getIt<KeyValueStorage>().get(KVKeys.kCloudType);
|
||||
if (value.isNone() && !integrationMode().isUnitTest) {
|
||||
if (value == null && !integrationMode().isUnitTest) {
|
||||
// if the cloud type is not set, then set it to AppFlowy Cloud as default.
|
||||
await useAppFlowyBetaCloudWithURL(
|
||||
kAppflowyCloudUrl,
|
||||
@@ -61,7 +60,7 @@ Future<AuthenticatorType> getAuthenticatorType() async {
|
||||
return AuthenticatorType.appflowyCloud;
|
||||
}
|
||||
|
||||
switch (value.getOrElse(() => "0")) {
|
||||
switch (value ?? "0") {
|
||||
case "0":
|
||||
return AuthenticatorType.local;
|
||||
case "1":
|
||||
@@ -177,16 +176,13 @@ AuthenticatorType currentCloudType() {
|
||||
return getIt<AppFlowyCloudSharedEnv>().authenticatorType;
|
||||
}
|
||||
|
||||
Future<void> _setAppFlowyCloudUrl(Option<String> url) async {
|
||||
await url.fold(
|
||||
() => getIt<KeyValueStorage>().set(KVKeys.kAppflowyCloudBaseURL, ""),
|
||||
(s) => getIt<KeyValueStorage>().set(KVKeys.kAppflowyCloudBaseURL, s),
|
||||
);
|
||||
Future<void> _setAppFlowyCloudUrl(String? url) async {
|
||||
await getIt<KeyValueStorage>().set(KVKeys.kAppflowyCloudBaseURL, url ?? '');
|
||||
}
|
||||
|
||||
Future<void> useSelfHostedAppFlowyCloudWithURL(String url) async {
|
||||
await _setAuthenticatorType(AuthenticatorType.appflowyCloudSelfHost);
|
||||
await _setAppFlowyCloudUrl(Some(url));
|
||||
await _setAppFlowyCloudUrl(url);
|
||||
}
|
||||
|
||||
Future<void> useAppFlowyBetaCloudWithURL(
|
||||
@@ -194,7 +190,7 @@ Future<void> useAppFlowyBetaCloudWithURL(
|
||||
AuthenticatorType authenticatorType,
|
||||
) async {
|
||||
await _setAuthenticatorType(authenticatorType);
|
||||
await _setAppFlowyCloudUrl(Some(url));
|
||||
await _setAppFlowyCloudUrl(url);
|
||||
}
|
||||
|
||||
Future<void> useLocalServer() async {
|
||||
@@ -206,7 +202,7 @@ Future<void> useSupabaseCloud({
|
||||
required String anonKey,
|
||||
}) async {
|
||||
await _setAuthenticatorType(AuthenticatorType.supabase);
|
||||
await setSupbaseServer(Some(url), Some(anonKey));
|
||||
await setSupabaseServer(url, anonKey);
|
||||
}
|
||||
|
||||
/// Use getIt<AppFlowyCloudSharedEnv>() to get the shared environment.
|
||||
@@ -314,10 +310,7 @@ Future<AppFlowyCloudConfiguration> getAppFlowyCloudConfig(
|
||||
Future<String> getAppFlowyCloudUrl() async {
|
||||
final result =
|
||||
await getIt<KeyValueStorage>().get(KVKeys.kAppflowyCloudBaseURL);
|
||||
return result.fold(
|
||||
() => kAppflowyCloudUrl,
|
||||
(url) => url,
|
||||
);
|
||||
return result ?? kAppflowyCloudUrl;
|
||||
}
|
||||
|
||||
Future<String> _getAppFlowyCloudWSUrl(String baseURL) async {
|
||||
@@ -339,27 +332,30 @@ Future<String> _getAppFlowyCloudGotrueUrl(String baseURL) async {
|
||||
return "$baseURL/gotrue";
|
||||
}
|
||||
|
||||
Future<void> setSupbaseServer(
|
||||
Option<String> url,
|
||||
Option<String> anonKey,
|
||||
Future<void> setSupabaseServer(
|
||||
String? url,
|
||||
String? anonKey,
|
||||
) async {
|
||||
assert(
|
||||
(url.isSome() && anonKey.isSome()) || (url.isNone() && anonKey.isNone()),
|
||||
(url != null && anonKey != null) || (url == null && anonKey == null),
|
||||
"Either both Supabase URL and anon key must be set, or both should be unset",
|
||||
);
|
||||
|
||||
await url.fold(
|
||||
() => getIt<KeyValueStorage>().remove(KVKeys.kSupabaseURL),
|
||||
(s) => getIt<KeyValueStorage>().set(KVKeys.kSupabaseURL, s),
|
||||
);
|
||||
await anonKey.fold(
|
||||
() => getIt<KeyValueStorage>().remove(KVKeys.kSupabaseAnonKey),
|
||||
(s) => getIt<KeyValueStorage>().set(KVKeys.kSupabaseAnonKey, s),
|
||||
);
|
||||
if (url == null) {
|
||||
await getIt<KeyValueStorage>().remove(KVKeys.kSupabaseURL);
|
||||
} else {
|
||||
await getIt<KeyValueStorage>().set(KVKeys.kSupabaseURL, url);
|
||||
}
|
||||
|
||||
if (anonKey == null) {
|
||||
await getIt<KeyValueStorage>().remove(KVKeys.kSupabaseAnonKey);
|
||||
} else {
|
||||
await getIt<KeyValueStorage>().set(KVKeys.kSupabaseAnonKey, anonKey);
|
||||
}
|
||||
}
|
||||
|
||||
Future<SupabaseConfiguration> getSupabaseCloudConfig() async {
|
||||
final url = await _getSupbaseUrl();
|
||||
final url = await _getSupabaseUrl();
|
||||
final anonKey = await _getSupabaseAnonKey();
|
||||
return SupabaseConfiguration(
|
||||
url: url,
|
||||
@@ -367,18 +363,12 @@ Future<SupabaseConfiguration> getSupabaseCloudConfig() async {
|
||||
);
|
||||
}
|
||||
|
||||
Future<String> _getSupbaseUrl() async {
|
||||
Future<String> _getSupabaseUrl() async {
|
||||
final result = await getIt<KeyValueStorage>().get(KVKeys.kSupabaseURL);
|
||||
return result.fold(
|
||||
() => "",
|
||||
(url) => url,
|
||||
);
|
||||
return result ?? '';
|
||||
}
|
||||
|
||||
Future<String> _getSupabaseAnonKey() async {
|
||||
final result = await getIt<KeyValueStorage>().get(KVKeys.kSupabaseAnonKey);
|
||||
return result.fold(
|
||||
() => "",
|
||||
(url) => url,
|
||||
);
|
||||
return result ?? '';
|
||||
}
|
||||
+1
-1
@@ -31,8 +31,8 @@ class UserProfileBloc extends Bloc<UserProfileEvent, UserProfileState> {
|
||||
);
|
||||
|
||||
final userProfile = userOrFailure.fold(
|
||||
(error) => null,
|
||||
(userProfilePB) => userProfilePB,
|
||||
(error) => null,
|
||||
);
|
||||
|
||||
if (workspaceSetting == null || userProfile == null) {
|
||||
|
||||
@@ -8,14 +8,17 @@ class AppBarBackButton extends StatelessWidget {
|
||||
const AppBarBackButton({
|
||||
super.key,
|
||||
this.onTap,
|
||||
this.padding,
|
||||
});
|
||||
|
||||
final VoidCallback? onTap;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppBarButton(
|
||||
onTap: onTap ?? () => Navigator.pop(context),
|
||||
padding: padding,
|
||||
child: const FlowySvg(
|
||||
FlowySvgs.m_app_bar_back_s,
|
||||
),
|
||||
@@ -73,10 +76,10 @@ class AppBarDoneButton extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppBarButton(
|
||||
isActionButton: true,
|
||||
onTap: onTap,
|
||||
padding: const EdgeInsets.fromLTRB(12, 12, 8, 12),
|
||||
child: FlowyText(
|
||||
LocaleKeys.button_Done.tr(),
|
||||
LocaleKeys.button_done.tr(),
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
fontWeight: FontWeight.w500,
|
||||
textAlign: TextAlign.right,
|
||||
@@ -85,6 +88,39 @@ class AppBarDoneButton extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class AppBarSaveButton extends StatelessWidget {
|
||||
const AppBarSaveButton({
|
||||
super.key,
|
||||
required this.onTap,
|
||||
this.enable = true,
|
||||
this.padding = const EdgeInsets.fromLTRB(12, 12, 8, 12),
|
||||
});
|
||||
|
||||
final VoidCallback onTap;
|
||||
final bool enable;
|
||||
final EdgeInsetsGeometry padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppBarButton(
|
||||
onTap: () {
|
||||
if (enable) {
|
||||
onTap();
|
||||
}
|
||||
},
|
||||
padding: padding,
|
||||
child: FlowyText(
|
||||
LocaleKeys.button_save.tr(),
|
||||
color: enable
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Theme.of(context).disabledColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppBarFilledDoneButton extends StatelessWidget {
|
||||
const AppBarFilledDoneButton({super.key, required this.onTap});
|
||||
|
||||
@@ -129,7 +165,7 @@ class AppBarMoreButton extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppBarButton(
|
||||
isActionButton: true,
|
||||
padding: const EdgeInsets.fromLTRB(12, 12, 8, 12),
|
||||
onTap: () => onTap(context),
|
||||
child: const FlowySvg(FlowySvgs.three_dots_s),
|
||||
);
|
||||
@@ -139,14 +175,14 @@ class AppBarMoreButton extends StatelessWidget {
|
||||
class AppBarButton extends StatelessWidget {
|
||||
const AppBarButton({
|
||||
super.key,
|
||||
this.isActionButton = false,
|
||||
required this.onTap,
|
||||
required this.child,
|
||||
this.padding,
|
||||
});
|
||||
|
||||
final VoidCallback onTap;
|
||||
final Widget child;
|
||||
final bool isActionButton;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -154,12 +190,7 @@ class AppBarButton extends StatelessWidget {
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: 12.0,
|
||||
bottom: 12.0,
|
||||
left: 12.0,
|
||||
right: isActionButton ? 12.0 : 8.0,
|
||||
),
|
||||
padding: padding ?? const EdgeInsets.all(12),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -13,7 +13,7 @@ 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-folder/view.pb.dart';
|
||||
import 'package:dartz/dartz.dart' hide State;
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -40,7 +40,7 @@ class MobileViewPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _MobileViewPageState extends State<MobileViewPage> {
|
||||
late final Future<Either<ViewPB, FlowyError>> future;
|
||||
late final Future<FlowyResult<ViewPB, FlowyError>> future;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
||||
@@ -7,3 +7,4 @@ export 'bottom_sheet_view_item_body.dart';
|
||||
export 'bottom_sheet_view_page.dart';
|
||||
export 'default_mobile_action_pane.dart';
|
||||
export 'show_mobile_bottom_sheet.dart';
|
||||
export 'show_transition_bottom_sheet.dart';
|
||||
+1
-1
@@ -49,7 +49,7 @@ class BottomSheetHeader extends StatelessWidget {
|
||||
color: Color(0xFF00BCF0),
|
||||
),
|
||||
text: FlowyText.medium(
|
||||
LocaleKeys.button_Done.tr(),
|
||||
LocaleKeys.button_done.tr(),
|
||||
color: Colors.white,
|
||||
fontSize: 16.0,
|
||||
),
|
||||
|
||||
+34
-31
@@ -1,9 +1,27 @@
|
||||
import 'package:appflowy/mobile/presentation/base/app_bar_actions.dart';
|
||||
import 'package:appflowy/plugins/base/drag_handler.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart' hide WidgetBuilder;
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
extension BottomSheetPaddingExtension on BuildContext {
|
||||
/// Calculates the total amount of space that should be added to the bottom of
|
||||
/// a bottom sheet
|
||||
double bottomSheetPadding({
|
||||
bool ignoreViewPadding = true,
|
||||
}) {
|
||||
final viewPadding = MediaQuery.viewPaddingOf(this);
|
||||
final viewInsets = MediaQuery.viewInsetsOf(this);
|
||||
double bottom = 0.0;
|
||||
if (!ignoreViewPadding) {
|
||||
bottom += viewPadding.bottom;
|
||||
}
|
||||
// for screens with 0 view padding, add some even more space
|
||||
bottom += viewPadding.bottom == 0 ? 28.0 : 16.0;
|
||||
bottom += viewInsets.bottom;
|
||||
return bottom;
|
||||
}
|
||||
}
|
||||
|
||||
Future<T?> showMobileBottomSheet<T>(
|
||||
BuildContext context, {
|
||||
required WidgetBuilder builder,
|
||||
@@ -16,7 +34,6 @@ Future<T?> showMobileBottomSheet<T>(
|
||||
bool showCloseButton = false,
|
||||
// this field is only used if showHeader is true
|
||||
String title = '',
|
||||
bool resizeToAvoidBottomInset = true,
|
||||
bool isScrollControlled = true,
|
||||
bool showDivider = true,
|
||||
bool useRootNavigator = false,
|
||||
@@ -42,7 +59,7 @@ Future<T?> showMobileBottomSheet<T>(
|
||||
|
||||
shape ??= const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Corners.s12Radius,
|
||||
top: Radius.circular(16),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -68,23 +85,20 @@ Future<T?> showMobileBottomSheet<T>(
|
||||
final Widget child = builder(context);
|
||||
|
||||
// if the children is only one, we don't need to wrap it with a column
|
||||
if (!showDragHandle &&
|
||||
!showHeader &&
|
||||
!showDivider &&
|
||||
!resizeToAvoidBottomInset) {
|
||||
if (!showDragHandle && !showHeader && !showDivider) {
|
||||
return child;
|
||||
}
|
||||
|
||||
// ----- header area -----
|
||||
if (showDragHandle) {
|
||||
children.add(
|
||||
const DragHandler(),
|
||||
const DragHandle(),
|
||||
);
|
||||
}
|
||||
|
||||
if (showHeader) {
|
||||
children.add(
|
||||
_Header(
|
||||
BottomSheetHeader(
|
||||
showCloseButton: showCloseButton,
|
||||
showBackButton: showBackButton,
|
||||
showDoneButton: showDoneButton,
|
||||
@@ -125,32 +139,20 @@ Future<T?> showMobileBottomSheet<T>(
|
||||
}
|
||||
|
||||
// ----- content area -----
|
||||
if (resizeToAvoidBottomInset) {
|
||||
children.add(
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: padding.top,
|
||||
left: padding.left,
|
||||
right: padding.right,
|
||||
bottom: padding.bottom + MediaQuery.of(context).viewInsets.bottom,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
children.add(child);
|
||||
}
|
||||
// add content padding and extra bottom padding
|
||||
children.add(
|
||||
Padding(
|
||||
padding:
|
||||
padding + EdgeInsets.only(bottom: context.bottomSheetPadding()),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
// ----- content area -----
|
||||
|
||||
if (children.length == 1) {
|
||||
return children.first;
|
||||
}
|
||||
|
||||
// add default padding
|
||||
children.add(
|
||||
VSpace(MediaQuery.of(context).padding.bottom == 0 ? 28.0 : 16.0),
|
||||
);
|
||||
|
||||
return useSafeArea
|
||||
? SafeArea(
|
||||
child: Column(
|
||||
@@ -166,8 +168,9 @@ Future<T?> showMobileBottomSheet<T>(
|
||||
);
|
||||
}
|
||||
|
||||
class _Header extends StatelessWidget {
|
||||
const _Header({
|
||||
class BottomSheetHeader extends StatelessWidget {
|
||||
const BottomSheetHeader({
|
||||
super.key,
|
||||
required this.showBackButton,
|
||||
required this.showCloseButton,
|
||||
required this.title,
|
||||
|
||||
+345
@@ -0,0 +1,345 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:appflowy/plugins/base/drag_handler.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:sheet/route.dart';
|
||||
import 'package:sheet/sheet.dart';
|
||||
|
||||
import 'show_mobile_bottom_sheet.dart';
|
||||
|
||||
Future<T?> showTransitionMobileBottomSheet<T>(
|
||||
BuildContext context, {
|
||||
required WidgetBuilder builder,
|
||||
bool useRootNavigator = false,
|
||||
EdgeInsets contentPadding = EdgeInsets.zero,
|
||||
Color? backgroundColor,
|
||||
// drag handle
|
||||
bool showDragHandle = false,
|
||||
// header
|
||||
bool showHeader = false,
|
||||
String title = '',
|
||||
bool showBackButton = false,
|
||||
bool showCloseButton = false,
|
||||
bool showDoneButton = false,
|
||||
bool showDivider = true,
|
||||
// stops
|
||||
double initialStop = 1.0,
|
||||
List<double>? stops,
|
||||
}) {
|
||||
assert(
|
||||
showHeader ||
|
||||
title.isEmpty &&
|
||||
!showCloseButton &&
|
||||
!showBackButton &&
|
||||
!showDoneButton &&
|
||||
!showDivider,
|
||||
);
|
||||
assert(!(showCloseButton && showBackButton));
|
||||
|
||||
backgroundColor ??= Theme.of(context).brightness == Brightness.light
|
||||
? const Color(0xFFF7F8FB)
|
||||
: const Color(0xFF626364);
|
||||
|
||||
return Navigator.of(
|
||||
context,
|
||||
rootNavigator: useRootNavigator,
|
||||
).push<T>(
|
||||
TransitionSheetRoute<T>(
|
||||
backgroundColor: backgroundColor,
|
||||
initialStop: initialStop,
|
||||
stops: stops,
|
||||
builder: (context) {
|
||||
final Widget child = builder(context);
|
||||
|
||||
// if the children is only one, we don't need to wrap it with a column
|
||||
if (!showDragHandle && !showHeader) {
|
||||
return child;
|
||||
}
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (showDragHandle) const DragHandle(),
|
||||
if (showHeader) ...[
|
||||
BottomSheetHeader(
|
||||
showCloseButton: showCloseButton,
|
||||
showBackButton: showBackButton,
|
||||
showDoneButton: showDoneButton,
|
||||
title: title,
|
||||
),
|
||||
if (showDivider)
|
||||
const Divider(
|
||||
height: 0.5,
|
||||
thickness: 0.5,
|
||||
),
|
||||
],
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: contentPadding,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// The top offset that will be displayed from the bottom route
|
||||
const double _kPreviousRouteVisibleOffset = 10.0;
|
||||
|
||||
/// Minimal distance from the top of the screen to the top of the previous route
|
||||
/// It will be used ff the top safe area is less than this value.
|
||||
/// In iPhones the top SafeArea is more or equal to this distance.
|
||||
const double _kSheetMinimalOffset = 10;
|
||||
|
||||
const Curve _kCupertinoSheetCurve = Curves.easeOutExpo;
|
||||
const Curve _kCupertinoTransitionCurve = Curves.linear;
|
||||
|
||||
/// Wraps the child into a cupertino modal sheet appearance. This is used to
|
||||
/// create a [SheetRoute].
|
||||
///
|
||||
/// Clip the child widget to rectangle with top rounded corners and adds
|
||||
/// top padding and top safe area.
|
||||
class _CupertinoSheetDecorationBuilder extends StatelessWidget {
|
||||
const _CupertinoSheetDecorationBuilder({
|
||||
required this.child,
|
||||
required this.topRadius,
|
||||
this.backgroundColor,
|
||||
});
|
||||
|
||||
/// The child contained by the modal sheet
|
||||
final Widget child;
|
||||
|
||||
/// The color to paint behind the child
|
||||
final Color? backgroundColor;
|
||||
|
||||
/// The top corners of this modal sheet are rounded by this Radius
|
||||
final Radius topRadius;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Builder(
|
||||
builder: (BuildContext context) {
|
||||
return Container(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.vertical(top: topRadius),
|
||||
color: backgroundColor,
|
||||
),
|
||||
child: MediaQuery.removePadding(
|
||||
context: context,
|
||||
removeTop: true,
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Customized CupertinoSheetRoute from the sheets package
|
||||
///
|
||||
/// A modal route that overlays a widget over the current route and animates
|
||||
/// it from the bottom with a cupertino modal sheet appearance
|
||||
///
|
||||
/// Clip the child widget to rectangle with top rounded corners and adds
|
||||
/// top padding and top safe area.
|
||||
class TransitionSheetRoute<T> extends SheetRoute<T> {
|
||||
TransitionSheetRoute({
|
||||
required WidgetBuilder builder,
|
||||
super.stops,
|
||||
double initialStop = 1.0,
|
||||
super.settings,
|
||||
Color? backgroundColor,
|
||||
super.maintainState = true,
|
||||
super.fit,
|
||||
}) : super(
|
||||
builder: (BuildContext context) {
|
||||
return _CupertinoSheetDecorationBuilder(
|
||||
backgroundColor: backgroundColor,
|
||||
topRadius: const Radius.circular(16),
|
||||
child: Builder(builder: builder),
|
||||
);
|
||||
},
|
||||
animationCurve: _kCupertinoSheetCurve,
|
||||
initialExtent: initialStop,
|
||||
);
|
||||
|
||||
@override
|
||||
bool get draggable => true;
|
||||
|
||||
final SheetController _sheetController = SheetController();
|
||||
|
||||
@override
|
||||
SheetController createSheetController() => _sheetController;
|
||||
|
||||
@override
|
||||
Color? get barrierColor => Colors.transparent;
|
||||
|
||||
@override
|
||||
bool get barrierDismissible => true;
|
||||
|
||||
@override
|
||||
Widget buildSheet(BuildContext context, Widget child) {
|
||||
final effectivePhysics = draggable
|
||||
? BouncingSheetPhysics(
|
||||
parent: SnapSheetPhysics(
|
||||
stops: stops ?? <double>[0, 1],
|
||||
parent: physics,
|
||||
),
|
||||
)
|
||||
: const NeverDraggableSheetPhysics();
|
||||
final MediaQueryData mediaQuery = MediaQuery.of(context);
|
||||
final double topMargin =
|
||||
math.max(_kSheetMinimalOffset, mediaQuery.padding.top) +
|
||||
_kPreviousRouteVisibleOffset;
|
||||
return Sheet.raw(
|
||||
initialExtent: initialExtent,
|
||||
decorationBuilder: decorationBuilder,
|
||||
fit: fit,
|
||||
maxExtent: mediaQuery.size.height - topMargin,
|
||||
physics: effectivePhysics,
|
||||
controller: sheetController,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildTransitions(
|
||||
BuildContext context,
|
||||
Animation<double> animation,
|
||||
Animation<double> secondaryAnimation,
|
||||
Widget child,
|
||||
) {
|
||||
final double topPadding = MediaQuery.of(context).padding.top;
|
||||
final double topOffset = math.max(_kSheetMinimalOffset, topPadding);
|
||||
return AnimatedBuilder(
|
||||
animation: secondaryAnimation,
|
||||
child: child,
|
||||
builder: (BuildContext context, Widget? child) {
|
||||
final double progress = secondaryAnimation.value;
|
||||
final double scale = 1 - progress / 10;
|
||||
final double distanceWithScale =
|
||||
(topOffset + _kPreviousRouteVisibleOffset) * 0.9;
|
||||
final Offset offset =
|
||||
Offset(0, progress * (topOffset - distanceWithScale));
|
||||
return Transform.translate(
|
||||
offset: offset,
|
||||
child: Transform.scale(
|
||||
scale: scale,
|
||||
alignment: Alignment.topCenter,
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool canDriveSecondaryTransitionForPreviousRoute(
|
||||
Route<dynamic> previousRoute,
|
||||
) =>
|
||||
true;
|
||||
|
||||
@override
|
||||
Widget buildSecondaryTransitionForPreviousRoute(
|
||||
BuildContext context,
|
||||
Animation<double> secondaryAnimation,
|
||||
Widget child,
|
||||
) {
|
||||
final Animation<double> delayAnimation = CurvedAnimation(
|
||||
parent: _sheetController.animation,
|
||||
curve: Interval(
|
||||
initialExtent == 1 ? 0 : initialExtent,
|
||||
1,
|
||||
),
|
||||
);
|
||||
|
||||
final Animation<double> secondaryAnimation = CurvedAnimation(
|
||||
parent: _sheetController.animation,
|
||||
curve: Interval(
|
||||
0,
|
||||
initialExtent,
|
||||
),
|
||||
);
|
||||
|
||||
return CupertinoSheetBottomRouteTransition(
|
||||
body: child,
|
||||
sheetAnimation: delayAnimation,
|
||||
secondaryAnimation: secondaryAnimation,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Animation for previous route when a [TransitionSheetRoute] enters/exits
|
||||
@visibleForTesting
|
||||
class CupertinoSheetBottomRouteTransition extends StatelessWidget {
|
||||
const CupertinoSheetBottomRouteTransition({
|
||||
super.key,
|
||||
required this.sheetAnimation,
|
||||
required this.secondaryAnimation,
|
||||
required this.body,
|
||||
});
|
||||
|
||||
final Widget body;
|
||||
|
||||
final Animation<double> sheetAnimation;
|
||||
final Animation<double> secondaryAnimation;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final double topPadding = MediaQuery.of(context).padding.top;
|
||||
final double topOffset = math.max(_kSheetMinimalOffset, topPadding);
|
||||
|
||||
final CurvedAnimation curvedAnimation = CurvedAnimation(
|
||||
parent: sheetAnimation,
|
||||
curve: _kCupertinoTransitionCurve,
|
||||
);
|
||||
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle.light,
|
||||
child: AnimatedBuilder(
|
||||
animation: secondaryAnimation,
|
||||
child: body,
|
||||
builder: (BuildContext context, Widget? child) {
|
||||
final double progress = curvedAnimation.value;
|
||||
final double scale = 1 - progress / 10;
|
||||
return Stack(
|
||||
children: <Widget>[
|
||||
Container(color: Colors.black),
|
||||
Transform.translate(
|
||||
offset: Offset(0, progress * topOffset),
|
||||
child: Transform.scale(
|
||||
scale: scale,
|
||||
alignment: Alignment.topCenter,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.lerp(
|
||||
Radius.zero,
|
||||
const Radius.circular(16.0),
|
||||
progress,
|
||||
)!,
|
||||
),
|
||||
child: ColorFiltered(
|
||||
colorFilter: ColorFilter.mode(
|
||||
(Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.grey
|
||||
: Colors.black)
|
||||
.withOpacity(secondaryAnimation.value * 0.1),
|
||||
BlendMode.srcOver,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -40,7 +40,7 @@ class MobileRowDetailCreateFieldButton extends StatelessWidget {
|
||||
LocaleKeys.grid_field_newProperty.tr(),
|
||||
fontSize: 15,
|
||||
),
|
||||
onPressed: () => showCreateFieldBottomSheet(context, viewId),
|
||||
onPressed: () => mobileCreateFieldWorkflow(context, viewId),
|
||||
icon: const FlowySvg(FlowySvgs.add_m),
|
||||
),
|
||||
);
|
||||
|
||||
+28
-13
@@ -1,7 +1,10 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/database/field/mobile_field_bottom_sheets.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
||||
import 'package:appflowy/util/field_type_extension.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class OptionTextField extends StatelessWidget {
|
||||
@@ -10,31 +13,43 @@ class OptionTextField extends StatelessWidget {
|
||||
required this.controller,
|
||||
required this.type,
|
||||
required this.onTextChanged,
|
||||
required this.onFieldTypeChanged,
|
||||
});
|
||||
|
||||
final TextEditingController controller;
|
||||
final FieldType type;
|
||||
final void Function(String value) onTextChanged;
|
||||
final void Function(FieldType value) onFieldTypeChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FlowyOptionTile.textField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
textFieldPadding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
onTextChanged: onTextChanged,
|
||||
leftIcon: Container(
|
||||
height: 38,
|
||||
width: 38,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: type.mobileIconBackgroundColor,
|
||||
),
|
||||
child: Center(
|
||||
child: FlowySvg(
|
||||
type.svgData,
|
||||
blendMode: null,
|
||||
size: const Size.square(22),
|
||||
leftIcon: GestureDetector(
|
||||
onTap: () async {
|
||||
final fieldType = await showFieldTypeGridBottomSheet(
|
||||
context,
|
||||
title: LocaleKeys.grid_field_editProperty.tr(),
|
||||
);
|
||||
if (fieldType != null) {
|
||||
onFieldTypeChanged(fieldType);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
height: 38,
|
||||
width: 38,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: type.mobileIconBackgroundColor,
|
||||
),
|
||||
child: Center(
|
||||
child: FlowySvg(
|
||||
type.svgData,
|
||||
blendMode: null,
|
||||
size: const Size.square(22),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ class _MobileDateCellEditScreenState extends State<MobileDateCellEditScreen> {
|
||||
children: [
|
||||
ColoredBox(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: const Center(child: DragHandler()),
|
||||
child: const Center(child: DragHandle()),
|
||||
),
|
||||
const MobileDateHeader(),
|
||||
_buildDatePicker(),
|
||||
|
||||
+21
-5
@@ -1,9 +1,9 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/app_bar.dart';
|
||||
import 'package:appflowy/mobile/presentation/database/field/mobile_full_field_editor.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_backend_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_backend_service.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_info.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_service.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/setting/field_visibility_extension.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -61,7 +61,7 @@ class _MobileEditPropertyScreenState extends State<MobileEditPropertyScreen> {
|
||||
body: MobileFieldEditor(
|
||||
mode: FieldOptionMode.edit,
|
||||
isPrimary: widget.field.isPrimary,
|
||||
defaultValues: _fieldOptionValues,
|
||||
defaultValues: FieldOptionValues.fromField(field: widget.field.field),
|
||||
actions: [
|
||||
widget.field.fieldSettings?.visibility.isVisibleState() ?? true
|
||||
? FieldOptionAction.hide
|
||||
@@ -69,9 +69,25 @@ class _MobileEditPropertyScreenState extends State<MobileEditPropertyScreen> {
|
||||
FieldOptionAction.duplicate,
|
||||
FieldOptionAction.delete,
|
||||
],
|
||||
onOptionValuesChanged: (newFieldOptionValues) {
|
||||
onOptionValuesChanged: (fieldOptionValues) async {
|
||||
await fieldService.updateField(name: fieldOptionValues.name);
|
||||
|
||||
await FieldBackendService.updateFieldType(
|
||||
viewId: widget.viewId,
|
||||
fieldId: widget.field.id,
|
||||
fieldType: fieldOptionValues.type,
|
||||
);
|
||||
|
||||
final data = fieldOptionValues.getTypeOptionData();
|
||||
if (data != null) {
|
||||
await FieldBackendService.updateFieldTypeOption(
|
||||
viewId: widget.viewId,
|
||||
fieldId: widget.field.id,
|
||||
typeOptionData: data,
|
||||
);
|
||||
}
|
||||
setState(() {
|
||||
_fieldOptionValues = newFieldOptionValues;
|
||||
_fieldOptionValues = fieldOptionValues;
|
||||
});
|
||||
},
|
||||
onAction: (action) {
|
||||
|
||||
+40
-30
@@ -29,23 +29,18 @@ const mobileSupportedFieldTypes = [
|
||||
FieldType.Checklist,
|
||||
];
|
||||
|
||||
/// Shows the field type grid and upon selection, allow users to edit the
|
||||
/// field's properties and saving it when the user clicks save.
|
||||
void showCreateFieldBottomSheet(
|
||||
BuildContext context,
|
||||
String viewId, {
|
||||
OrderObjectPositionPB? position,
|
||||
Future<FieldType?> showFieldTypeGridBottomSheet(
|
||||
BuildContext context, {
|
||||
required String title,
|
||||
}) {
|
||||
showMobileBottomSheet(
|
||||
return showMobileBottomSheet<FieldType>(
|
||||
context,
|
||||
padding: EdgeInsets.zero,
|
||||
showHeader: true,
|
||||
showDragHandle: true,
|
||||
showCloseButton: true,
|
||||
elevation: 20,
|
||||
title: LocaleKeys.grid_field_newProperty.tr(),
|
||||
title: title,
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
barrierColor: Colors.transparent,
|
||||
enableDraggableScrollable: true,
|
||||
builder: (context) {
|
||||
final typeOptionMenuItemValue = mobileSupportedFieldTypes
|
||||
@@ -55,24 +50,8 @@ void showCreateFieldBottomSheet(
|
||||
backgroundColor: fieldType.mobileIconBackgroundColor,
|
||||
text: fieldType.i18n,
|
||||
icon: fieldType.svgData,
|
||||
onTap: (_, fieldType) async {
|
||||
final optionValues = await context.push<FieldOptionValues>(
|
||||
Uri(
|
||||
path: MobileNewPropertyScreen.routeName,
|
||||
queryParameters: {
|
||||
MobileNewPropertyScreen.argViewId: viewId,
|
||||
MobileNewPropertyScreen.argFieldTypeId:
|
||||
fieldType.value.toString(),
|
||||
},
|
||||
).toString(),
|
||||
);
|
||||
if (optionValues != null) {
|
||||
await optionValues.create(viewId: viewId, position: position);
|
||||
if (context.mounted) {
|
||||
context.pop();
|
||||
}
|
||||
}
|
||||
},
|
||||
onTap: (context, fieldType) =>
|
||||
Navigator.of(context).pop(fieldType),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
@@ -87,6 +66,37 @@ void showCreateFieldBottomSheet(
|
||||
);
|
||||
}
|
||||
|
||||
/// Shows the field type grid and upon selection, allow users to edit the
|
||||
/// field's properties and saving it when the user clicks save.
|
||||
void mobileCreateFieldWorkflow(
|
||||
BuildContext context,
|
||||
String viewId, {
|
||||
OrderObjectPositionPB? position,
|
||||
}) async {
|
||||
final fieldType = await showFieldTypeGridBottomSheet(
|
||||
context,
|
||||
title: LocaleKeys.grid_field_newProperty.tr(),
|
||||
);
|
||||
if (fieldType == null || !context.mounted) {
|
||||
return;
|
||||
}
|
||||
final optionValues = await context.push<FieldOptionValues>(
|
||||
Uri(
|
||||
path: MobileNewPropertyScreen.routeName,
|
||||
queryParameters: {
|
||||
MobileNewPropertyScreen.argViewId: viewId,
|
||||
MobileNewPropertyScreen.argFieldTypeId: fieldType.value.toString(),
|
||||
},
|
||||
).toString(),
|
||||
);
|
||||
if (optionValues != null) {
|
||||
await optionValues.create(viewId: viewId, position: position);
|
||||
}
|
||||
if (context.mounted) {
|
||||
context.pop();
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to edit a field.
|
||||
Future<FieldOptionValues?> showEditFieldScreen(
|
||||
BuildContext context,
|
||||
@@ -106,18 +116,18 @@ Future<FieldOptionValues?> showEditFieldScreen(
|
||||
void showQuickEditField(
|
||||
BuildContext context,
|
||||
String viewId,
|
||||
FieldController fieldController,
|
||||
FieldInfo fieldInfo,
|
||||
) {
|
||||
showMobileBottomSheet(
|
||||
context,
|
||||
padding: EdgeInsets.zero,
|
||||
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
|
||||
resizeToAvoidBottomInset: true,
|
||||
showDragHandle: true,
|
||||
builder: (context) {
|
||||
return SingleChildScrollView(
|
||||
child: QuickEditField(
|
||||
viewId: viewId,
|
||||
fieldController: fieldController,
|
||||
fieldInfo: fieldInfo,
|
||||
),
|
||||
);
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ class _MobileFieldPickerListState extends State<MobileFieldPickerList> {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const DragHandler(),
|
||||
const DragHandle(),
|
||||
_Header(
|
||||
title: widget.title,
|
||||
onDone: (context) => context.pop(newFieldId),
|
||||
|
||||
+19
-37
@@ -5,16 +5,14 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/flowy_search_text_field.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/option_color_list.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/type_option_menu_item.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||
import 'package:appflowy/mobile/presentation/database/card/card_detail/widgets/widgets.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
||||
import 'package:appflowy/plugins/base/drag_handler.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_service.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/type_option/number_format_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/type_option/date/date_time_format.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell_editor/extension.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||
import 'package:appflowy/util/field_type_extension.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
@@ -223,6 +221,18 @@ class _MobileFieldEditorState extends State<MobileFieldEditor> {
|
||||
isFieldNameChanged = true;
|
||||
_updateOptionValues(name: value);
|
||||
},
|
||||
onFieldTypeChanged: (type) {
|
||||
setState(
|
||||
() {
|
||||
if (widget.mode == FieldOptionMode.add &&
|
||||
!isFieldNameChanged) {
|
||||
controller.text = type.i18n;
|
||||
_updateOptionValues(name: type.i18n);
|
||||
}
|
||||
_updateOptionValues(type: type);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
const _Divider(),
|
||||
if (!widget.isPrimary) ...[
|
||||
@@ -444,42 +454,14 @@ class _PropertyType extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
showMobileBottomSheet(
|
||||
onTap: () async {
|
||||
final fieldType = await showFieldTypeGridBottomSheet(
|
||||
context,
|
||||
padding: EdgeInsets.zero,
|
||||
showHeader: true,
|
||||
showDragHandle: true,
|
||||
showCloseButton: true,
|
||||
elevation: 20,
|
||||
title: LocaleKeys.grid_field_editProperty.tr(),
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
barrierColor: Colors.transparent,
|
||||
enableDraggableScrollable: true,
|
||||
builder: (context) {
|
||||
final typeOptionMenuItemValue = mobileSupportedFieldTypes
|
||||
.map(
|
||||
(fieldType) => TypeOptionMenuItemValue(
|
||||
value: fieldType,
|
||||
backgroundColor: fieldType.mobileIconBackgroundColor,
|
||||
text: fieldType.i18n,
|
||||
icon: fieldType.svgData,
|
||||
onTap: (_, fieldType) {
|
||||
onSelected(fieldType);
|
||||
context.pop();
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
return Padding(
|
||||
padding: EdgeInsets.all(16 * context.scale),
|
||||
child: TypeOptionMenu<FieldType>(
|
||||
values: typeOptionMenuItemValue,
|
||||
scaleFactor: context.scale,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
if (fieldType != null) {
|
||||
onSelected(fieldType);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -711,7 +693,7 @@ class _NumberFormatListState extends State<_NumberFormatList> {
|
||||
controller: widget.scrollController,
|
||||
children: [
|
||||
const Center(
|
||||
child: DragHandler(),
|
||||
child: DragHandle(),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||
|
||||
+124
-141
@@ -2,27 +2,29 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/database/card/card_detail/widgets/widgets.dart';
|
||||
import 'package:appflowy/mobile/presentation/database/field/mobile_field_bottom_sheets.dart';
|
||||
import 'package:appflowy/mobile/presentation/database/field/mobile_full_field_editor.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_backend_service.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_editor_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_backend_service.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_info.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_service.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/setting/field_visibility_extension.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
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';
|
||||
import 'package:protobuf/protobuf.dart' hide FieldInfo;
|
||||
|
||||
class QuickEditField extends StatefulWidget {
|
||||
const QuickEditField({
|
||||
super.key,
|
||||
required this.viewId,
|
||||
required this.fieldController,
|
||||
required this.fieldInfo,
|
||||
});
|
||||
|
||||
final String viewId;
|
||||
final FieldController fieldController;
|
||||
final FieldInfo fieldInfo;
|
||||
|
||||
@override
|
||||
@@ -38,14 +40,10 @@ class _QuickEditFieldState extends State<QuickEditField> {
|
||||
);
|
||||
|
||||
late FieldVisibility fieldVisibility;
|
||||
late FieldOptionValues _fieldOptionValues;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_fieldOptionValues =
|
||||
FieldOptionValues.fromField(field: widget.fieldInfo.field);
|
||||
fieldVisibility = widget.fieldInfo.fieldSettings?.visibility ??
|
||||
FieldVisibility.AlwaysShown;
|
||||
controller.text = widget.fieldInfo.field.name;
|
||||
@@ -59,140 +57,125 @@ class _QuickEditFieldState extends State<QuickEditField> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const VSpace(16),
|
||||
OptionTextField(
|
||||
controller: controller,
|
||||
type: _fieldOptionValues.type,
|
||||
onTextChanged: (text) async {
|
||||
await service.updateName(text);
|
||||
},
|
||||
),
|
||||
const _Divider(),
|
||||
FlowyOptionTile.text(
|
||||
text: LocaleKeys.grid_field_editProperty.tr(),
|
||||
leftIcon: const FlowySvg(FlowySvgs.m_field_edit_s),
|
||||
onTap: () async {
|
||||
widget.fieldInfo.field.freeze();
|
||||
final field = widget.fieldInfo.field.rebuild((field) {
|
||||
field.name = controller.text;
|
||||
field.fieldType = _fieldOptionValues.type;
|
||||
field.typeOptionData =
|
||||
_fieldOptionValues.getTypeOptionData() ?? [];
|
||||
});
|
||||
final fieldOptionValues = await showEditFieldScreen(
|
||||
context,
|
||||
widget.viewId,
|
||||
widget.fieldInfo.copyWith(field: field),
|
||||
);
|
||||
if (fieldOptionValues != null) {
|
||||
if (fieldOptionValues.name != _fieldOptionValues.name) {
|
||||
await service.updateName(fieldOptionValues.name);
|
||||
}
|
||||
|
||||
if (fieldOptionValues.type != _fieldOptionValues.type) {
|
||||
await FieldBackendService.updateFieldType(
|
||||
viewId: widget.viewId,
|
||||
fieldId: widget.fieldInfo.id,
|
||||
fieldType: fieldOptionValues.type,
|
||||
);
|
||||
}
|
||||
|
||||
final data = fieldOptionValues.getTypeOptionData();
|
||||
if (data != null) {
|
||||
await FieldBackendService.updateFieldTypeOption(
|
||||
viewId: widget.viewId,
|
||||
fieldId: widget.fieldInfo.id,
|
||||
typeOptionData: data,
|
||||
);
|
||||
}
|
||||
setState(() {
|
||||
_fieldOptionValues = fieldOptionValues;
|
||||
controller.text = fieldOptionValues.name;
|
||||
});
|
||||
} else {
|
||||
if (mounted) {
|
||||
context.pop();
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
if (!widget.fieldInfo.isPrimary)
|
||||
FlowyOptionTile.text(
|
||||
showTopBorder: false,
|
||||
text: fieldVisibility.isVisibleState()
|
||||
? LocaleKeys.grid_field_hide.tr()
|
||||
: LocaleKeys.grid_field_show.tr(),
|
||||
leftIcon: const FlowySvg(FlowySvgs.m_field_hide_s),
|
||||
onTap: () async {
|
||||
context.pop();
|
||||
if (fieldVisibility.isVisibleState()) {
|
||||
await service.hide();
|
||||
} else {
|
||||
await service.hide();
|
||||
}
|
||||
},
|
||||
),
|
||||
if (!widget.fieldInfo.isPrimary)
|
||||
FlowyOptionTile.text(
|
||||
showTopBorder: false,
|
||||
text: LocaleKeys.grid_field_insertLeft.tr(),
|
||||
leftIcon: const FlowySvg(FlowySvgs.m_filed_insert_left_s),
|
||||
onTap: () async {
|
||||
context.pop();
|
||||
showCreateFieldBottomSheet(
|
||||
context,
|
||||
widget.viewId,
|
||||
position: OrderObjectPositionPB(
|
||||
position: OrderObjectPositionTypePB.Before,
|
||||
objectId: widget.fieldInfo.id,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
FlowyOptionTile.text(
|
||||
showTopBorder: false,
|
||||
text: LocaleKeys.grid_field_insertRight.tr(),
|
||||
leftIcon: const FlowySvg(FlowySvgs.m_filed_insert_right_s),
|
||||
onTap: () async {
|
||||
context.pop();
|
||||
showCreateFieldBottomSheet(
|
||||
context,
|
||||
widget.viewId,
|
||||
position: OrderObjectPositionPB(
|
||||
position: OrderObjectPositionTypePB.After,
|
||||
objectId: widget.fieldInfo.id,
|
||||
return BlocProvider(
|
||||
create: (_) => FieldEditorBloc(
|
||||
viewId: widget.viewId,
|
||||
fieldController: widget.fieldController,
|
||||
field: widget.fieldInfo.field,
|
||||
)..add(const FieldEditorEvent.initial()),
|
||||
child: BlocConsumer<FieldEditorBloc, FieldEditorState>(
|
||||
listenWhen: (previous, current) =>
|
||||
previous.field.name != current.field.name,
|
||||
listener: (context, state) => controller.text = state.field.name,
|
||||
builder: (context, state) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const VSpace(16),
|
||||
OptionTextField(
|
||||
controller: controller,
|
||||
type: state.field.fieldType,
|
||||
onTextChanged: (text) {
|
||||
context
|
||||
.read<FieldEditorBloc>()
|
||||
.add(FieldEditorEvent.renameField(text));
|
||||
},
|
||||
onFieldTypeChanged: (fieldType) {
|
||||
context
|
||||
.read<FieldEditorBloc>()
|
||||
.add(FieldEditorEvent.switchFieldType(fieldType));
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (!widget.fieldInfo.isPrimary) ...[
|
||||
FlowyOptionTile.text(
|
||||
showTopBorder: false,
|
||||
text: LocaleKeys.button_duplicate.tr(),
|
||||
leftIcon: const FlowySvg(FlowySvgs.m_field_copy_s),
|
||||
onTap: () async {
|
||||
context.pop();
|
||||
await service.duplicate();
|
||||
},
|
||||
),
|
||||
FlowyOptionTile.text(
|
||||
showTopBorder: false,
|
||||
text: LocaleKeys.button_delete.tr(),
|
||||
textColor: Theme.of(context).colorScheme.error,
|
||||
leftIcon: FlowySvg(
|
||||
FlowySvgs.m_field_delete_s,
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
onTap: () async {
|
||||
context.pop();
|
||||
await service.delete();
|
||||
},
|
||||
),
|
||||
],
|
||||
],
|
||||
const _Divider(),
|
||||
FlowyOptionTile.text(
|
||||
text: LocaleKeys.grid_field_editProperty.tr(),
|
||||
leftIcon: const FlowySvg(FlowySvgs.m_field_edit_s),
|
||||
onTap: () {
|
||||
showEditFieldScreen(
|
||||
context,
|
||||
widget.viewId,
|
||||
state.field,
|
||||
);
|
||||
context.pop();
|
||||
},
|
||||
),
|
||||
if (!widget.fieldInfo.isPrimary)
|
||||
FlowyOptionTile.text(
|
||||
showTopBorder: false,
|
||||
text: fieldVisibility.isVisibleState()
|
||||
? LocaleKeys.grid_field_hide.tr()
|
||||
: LocaleKeys.grid_field_show.tr(),
|
||||
leftIcon: const FlowySvg(FlowySvgs.m_field_hide_s),
|
||||
onTap: () async {
|
||||
context.pop();
|
||||
if (fieldVisibility.isVisibleState()) {
|
||||
await service.hide();
|
||||
} else {
|
||||
await service.hide();
|
||||
}
|
||||
},
|
||||
),
|
||||
if (!widget.fieldInfo.isPrimary)
|
||||
FlowyOptionTile.text(
|
||||
showTopBorder: false,
|
||||
text: LocaleKeys.grid_field_insertLeft.tr(),
|
||||
leftIcon: const FlowySvg(FlowySvgs.m_filed_insert_left_s),
|
||||
onTap: () {
|
||||
context.pop();
|
||||
mobileCreateFieldWorkflow(
|
||||
context,
|
||||
widget.viewId,
|
||||
position: OrderObjectPositionPB(
|
||||
position: OrderObjectPositionTypePB.Before,
|
||||
objectId: widget.fieldInfo.id,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
FlowyOptionTile.text(
|
||||
showTopBorder: false,
|
||||
text: LocaleKeys.grid_field_insertRight.tr(),
|
||||
leftIcon: const FlowySvg(FlowySvgs.m_filed_insert_right_s),
|
||||
onTap: () {
|
||||
context.pop();
|
||||
mobileCreateFieldWorkflow(
|
||||
context,
|
||||
widget.viewId,
|
||||
position: OrderObjectPositionPB(
|
||||
position: OrderObjectPositionTypePB.After,
|
||||
objectId: widget.fieldInfo.id,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (!widget.fieldInfo.isPrimary) ...[
|
||||
FlowyOptionTile.text(
|
||||
showTopBorder: false,
|
||||
text: LocaleKeys.button_duplicate.tr(),
|
||||
leftIcon: const FlowySvg(FlowySvgs.m_field_copy_s),
|
||||
onTap: () {
|
||||
context.pop();
|
||||
service.duplicate();
|
||||
},
|
||||
),
|
||||
FlowyOptionTile.text(
|
||||
showTopBorder: false,
|
||||
text: LocaleKeys.button_delete.tr(),
|
||||
textColor: Theme.of(context).colorScheme.error,
|
||||
leftIcon: FlowySvg(
|
||||
FlowySvgs.m_field_delete_s,
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
onTap: () {
|
||||
context.pop();
|
||||
service.delete();
|
||||
},
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+12
-3
@@ -2,6 +2,7 @@ import 'dart:ui';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/flowy_option_tile.dart';
|
||||
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||
@@ -61,6 +62,7 @@ class _MobileDatabaseFieldListBody extends StatelessWidget {
|
||||
if (state.fieldContexts.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final fields = [...state.fieldContexts];
|
||||
final firstField = fields.removeAt(0);
|
||||
final firstCell = DatabaseFieldListTile(
|
||||
@@ -68,7 +70,7 @@ class _MobileDatabaseFieldListBody extends StatelessWidget {
|
||||
viewId: viewId,
|
||||
fieldController: databaseController.fieldController,
|
||||
fieldInfo: firstField,
|
||||
showTopBorder: true,
|
||||
showTopBorder: false,
|
||||
);
|
||||
final cells = fields
|
||||
.mapIndexed(
|
||||
@@ -124,9 +126,16 @@ class _MobileDatabaseFieldListBody extends StatelessWidget {
|
||||
children: [
|
||||
_divider(),
|
||||
_NewDatabaseFieldTile(viewId: viewId),
|
||||
VSpace(
|
||||
context.bottomSheetPadding(
|
||||
ignoreViewPadding: false,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: null,
|
||||
: VSpace(
|
||||
context.bottomSheetPadding(ignoreViewPadding: false),
|
||||
),
|
||||
itemCount: cells.length,
|
||||
itemBuilder: (context, index) => cells[index],
|
||||
);
|
||||
@@ -204,7 +213,7 @@ class _NewDatabaseFieldTile extends StatelessWidget {
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
textColor: Theme.of(context).hintColor,
|
||||
onTap: () => showCreateFieldBottomSheet(context, viewId),
|
||||
onTap: () => mobileCreateFieldWorkflow(context, viewId),
|
||||
);
|
||||
}
|
||||
}
|
||||
+553
@@ -0,0 +1,553 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/app_bar_actions.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
||||
import 'package:appflowy/plugins/database/grid/application/sort/sort_editor_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/sort/sort_info.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
|
||||
import 'database_sort_bottom_sheet_cubit.dart';
|
||||
|
||||
class MobileSortEditor extends StatefulWidget {
|
||||
const MobileSortEditor({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<MobileSortEditor> createState() => _MobileSortEditorState();
|
||||
}
|
||||
|
||||
class _MobileSortEditorState extends State<MobileSortEditor> {
|
||||
final PageController _pageController = PageController();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_pageController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => MobileSortEditorCubit(
|
||||
pageController: _pageController,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
const _Header(),
|
||||
SizedBox(
|
||||
height: 400, //314,
|
||||
child: PageView.builder(
|
||||
controller: _pageController,
|
||||
itemCount: 2,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
return index == 0
|
||||
? Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).padding.bottom,
|
||||
),
|
||||
child: const _Overview(),
|
||||
)
|
||||
: const _SortDetail();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Header extends StatelessWidget {
|
||||
const _Header();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<MobileSortEditorCubit, MobileSortEditorState>(
|
||||
builder: (context, state) {
|
||||
return SizedBox(
|
||||
height: 44.0,
|
||||
child: Stack(
|
||||
children: [
|
||||
if (state.showBackButton)
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: AppBarBackButton(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 12,
|
||||
horizontal: 16,
|
||||
),
|
||||
onTap: () => context
|
||||
.read<MobileSortEditorCubit>()
|
||||
.returnToOverview(),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
child: FlowyText.medium(
|
||||
LocaleKeys.grid_settings_sort.tr(),
|
||||
fontSize: 16.0,
|
||||
),
|
||||
),
|
||||
if (state.isCreatingNewSort)
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: AppBarSaveButton(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 12,
|
||||
horizontal: 16,
|
||||
),
|
||||
enable: state.newSortFieldId != null,
|
||||
onTap: () {
|
||||
_tryCreateSort(context, state);
|
||||
context.read<MobileSortEditorCubit>().returnToOverview();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _tryCreateSort(BuildContext context, MobileSortEditorState state) {
|
||||
if (state.newSortFieldId != null && state.newSortCondition != null) {
|
||||
context.read<SortEditorBloc>().add(
|
||||
SortEditorEvent.createSort(
|
||||
fieldId: state.newSortFieldId!,
|
||||
condition: state.newSortCondition!,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _Overview extends StatelessWidget {
|
||||
const _Overview();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SortEditorBloc, SortEditorState>(
|
||||
builder: (context, state) {
|
||||
return Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: state.sortInfos.isEmpty
|
||||
? Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
FlowySvg(
|
||||
FlowySvgs.sort_descending_s,
|
||||
size: const Size.square(60),
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
FlowyText(
|
||||
LocaleKeys.grid_sort_empty.tr(),
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: ReorderableListView.builder(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
),
|
||||
proxyDecorator: (child, index, animation) => Material(
|
||||
color: Colors.transparent,
|
||||
child: child,
|
||||
),
|
||||
onReorder: (oldIndex, newIndex) => context
|
||||
.read<SortEditorBloc>()
|
||||
.add(SortEditorEvent.reorderSort(oldIndex, newIndex)),
|
||||
itemCount: state.sortInfos.length,
|
||||
itemBuilder: (context, index) => _SortItem(
|
||||
key: ValueKey("sort_item_$index"),
|
||||
sort: state.sortInfos[index],
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 44,
|
||||
width: double.infinity,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 14),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.fromBorderSide(
|
||||
BorderSide(
|
||||
width: 0.5,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
borderRadius: Corners.s10Border,
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
final firstField = context
|
||||
.read<SortEditorBloc>()
|
||||
.state
|
||||
.creatableFields
|
||||
.firstOrNull;
|
||||
if (firstField == null) {
|
||||
Fluttertoast.showToast(
|
||||
msg: LocaleKeys.grid_sort_cannotFindCreatableField.tr(),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
} else {
|
||||
context.read<MobileSortEditorCubit>().startCreatingSort();
|
||||
}
|
||||
},
|
||||
borderRadius: Corners.s10Border,
|
||||
child: Center(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const FlowySvg(
|
||||
FlowySvgs.add_s,
|
||||
size: Size.square(16),
|
||||
),
|
||||
const HSpace(6.0),
|
||||
FlowyText(
|
||||
LocaleKeys.grid_sort_addSort.tr(),
|
||||
fontSize: 15,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SortItem extends StatelessWidget {
|
||||
const _SortItem({super.key, required this.sort});
|
||||
|
||||
final SortInfo sort;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(
|
||||
vertical: 4.0,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).hoverColor,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => context
|
||||
.read<MobileSortEditorCubit>()
|
||||
.startEditingSort(sort.sortId),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 14,
|
||||
horizontal: 8,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: Expanded(
|
||||
child: FlowyText.medium(
|
||||
LocaleKeys.grid_sort_by.tr(),
|
||||
fontSize: 15,
|
||||
),
|
||||
),
|
||||
),
|
||||
const VSpace(10),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 44,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.fromBorderSide(
|
||||
BorderSide(
|
||||
width: 0.5,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
borderRadius: Corners.s10Border,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Center(
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: FlowyText(
|
||||
sort.fieldInfo.name,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const HSpace(6.0),
|
||||
FlowySvg(
|
||||
FlowySvgs.icon_right_small_ccm_outlined_s,
|
||||
size: const Size.square(14),
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const HSpace(6),
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 44,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.fromBorderSide(
|
||||
BorderSide(
|
||||
width: 0.5,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
borderRadius: Corners.s10Border,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
padding: const EdgeInsetsDirectional.only(
|
||||
start: 12,
|
||||
end: 10,
|
||||
),
|
||||
child: Center(
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: FlowyText(
|
||||
sort.sortPB.condition.name,
|
||||
),
|
||||
),
|
||||
const HSpace(6.0),
|
||||
FlowySvg(
|
||||
FlowySvgs.icon_right_small_ccm_outlined_s,
|
||||
size: const Size.square(14),
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 8,
|
||||
top: 9,
|
||||
child: InkWell(
|
||||
onTap: () => context
|
||||
.read<SortEditorBloc>()
|
||||
.add(SortEditorEvent.deleteSort(sort)),
|
||||
// steal from the container LongClickReorderWidget thing
|
||||
onLongPress: () {},
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: SizedBox.square(
|
||||
dimension: 34,
|
||||
child: Center(
|
||||
child: FlowySvg(
|
||||
FlowySvgs.trash_m,
|
||||
size: const Size.square(18),
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SortDetail extends StatelessWidget {
|
||||
const _SortDetail();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isCreatingNewSort =
|
||||
context.read<MobileSortEditorCubit>().state.isCreatingNewSort;
|
||||
|
||||
return isCreatingNewSort
|
||||
? const _SortDetailContent()
|
||||
: BlocSelector<SortEditorBloc, SortEditorState, SortInfo>(
|
||||
selector: (state) => state.sortInfos.firstWhere(
|
||||
(sortInfo) =>
|
||||
sortInfo.sortId ==
|
||||
context.read<MobileSortEditorCubit>().state.editingSortId,
|
||||
),
|
||||
builder: (context, sortInfo) {
|
||||
return _SortDetailContent(sortInfo: sortInfo);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SortDetailContent extends StatelessWidget {
|
||||
const _SortDetailContent({
|
||||
this.sortInfo,
|
||||
});
|
||||
|
||||
final SortInfo? sortInfo;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const VSpace(4),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: DefaultTabController(
|
||||
length: 2,
|
||||
initialIndex: sortInfo == null
|
||||
? 0
|
||||
: sortInfo!.sortPB.condition == SortConditionPB.Ascending
|
||||
? 0
|
||||
: 1,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(3.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: Theme.of(context).hoverColor,
|
||||
),
|
||||
child: TabBar(
|
||||
indicatorSize: TabBarIndicatorSize.label,
|
||||
labelPadding: EdgeInsets.zero,
|
||||
padding: EdgeInsets.zero,
|
||||
indicatorWeight: 0,
|
||||
indicator: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
splashFactory: NoSplash.splashFactory,
|
||||
overlayColor: const MaterialStatePropertyAll(
|
||||
Colors.transparent,
|
||||
),
|
||||
onTap: (index) {
|
||||
final newCondition = index == 0
|
||||
? SortConditionPB.Ascending
|
||||
: SortConditionPB.Descending;
|
||||
_changeCondition(context, newCondition);
|
||||
},
|
||||
tabs: [
|
||||
Tab(
|
||||
height: 34,
|
||||
child: Center(
|
||||
child: FlowyText(
|
||||
LocaleKeys.grid_sort_ascending.tr(),
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
Tab(
|
||||
height: 34,
|
||||
child: Center(
|
||||
child: FlowyText(
|
||||
LocaleKeys.grid_sort_descending.tr(),
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const VSpace(20),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: FlowyText(
|
||||
LocaleKeys.grid_settings_sortBy.tr().toUpperCase(),
|
||||
fontSize: 13,
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
),
|
||||
const VSpace(4.0),
|
||||
const Divider(
|
||||
height: 0.5,
|
||||
thickness: 0.5,
|
||||
),
|
||||
Expanded(
|
||||
child: BlocBuilder<SortEditorBloc, SortEditorState>(
|
||||
builder: (context, state) {
|
||||
final fields = state.allFields
|
||||
.where(
|
||||
(field) =>
|
||||
field.canCreateSort ||
|
||||
sortInfo != null && sortInfo!.fieldId == field.id,
|
||||
)
|
||||
.toList();
|
||||
return ListView.builder(
|
||||
itemCount: fields.length,
|
||||
itemBuilder: (context, index) {
|
||||
final fieldInfo = fields[index];
|
||||
final isSelected = sortInfo == null
|
||||
? context
|
||||
.watch<MobileSortEditorCubit>()
|
||||
.state
|
||||
.newSortFieldId ==
|
||||
fieldInfo.id
|
||||
: sortInfo!.fieldId == fieldInfo.id;
|
||||
return FlowyOptionTile.checkbox(
|
||||
text: fieldInfo.field.name,
|
||||
isSelected: isSelected,
|
||||
showTopBorder: false,
|
||||
onTap: () {
|
||||
if (!isSelected) {
|
||||
_changeFieldId(context, fieldInfo.id);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _changeCondition(BuildContext context, SortConditionPB newCondition) {
|
||||
if (sortInfo == null) {
|
||||
context.read<MobileSortEditorCubit>().changeSortCondition(newCondition);
|
||||
} else {
|
||||
context.read<SortEditorBloc>().add(
|
||||
SortEditorEvent.editSort(
|
||||
sortId: sortInfo!.sortId,
|
||||
condition: newCondition,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _changeFieldId(BuildContext context, String newFieldId) {
|
||||
if (sortInfo == null) {
|
||||
context.read<MobileSortEditorCubit>().changeFieldId(newFieldId);
|
||||
} else {
|
||||
context.read<SortEditorBloc>().add(
|
||||
SortEditorEvent.editSort(
|
||||
sortId: sortInfo!.sortId,
|
||||
fieldId: newFieldId,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'database_sort_bottom_sheet_cubit.freezed.dart';
|
||||
|
||||
class MobileSortEditorCubit extends Cubit<MobileSortEditorState> {
|
||||
MobileSortEditorCubit({
|
||||
required this.pageController,
|
||||
}) : super(MobileSortEditorState.initial());
|
||||
|
||||
final PageController pageController;
|
||||
|
||||
void returnToOverview() {
|
||||
_animateToPage(0);
|
||||
emit(MobileSortEditorState.initial());
|
||||
}
|
||||
|
||||
void startCreatingSort() {
|
||||
_animateToPage(1);
|
||||
emit(
|
||||
state.copyWith(
|
||||
showBackButton: true,
|
||||
isCreatingNewSort: true,
|
||||
newSortCondition: SortConditionPB.Ascending,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void startEditingSort(String sortId) {
|
||||
_animateToPage(1);
|
||||
emit(
|
||||
state.copyWith(
|
||||
showBackButton: true,
|
||||
editingSortId: sortId,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// only used when creating a new sort
|
||||
void changeFieldId(String fieldId) {
|
||||
emit(state.copyWith(newSortFieldId: fieldId));
|
||||
}
|
||||
|
||||
/// only used when creating a new sort
|
||||
void changeSortCondition(SortConditionPB condition) {
|
||||
emit(state.copyWith(newSortCondition: condition));
|
||||
}
|
||||
|
||||
Future<void> _animateToPage(int page) async {
|
||||
return pageController.animateToPage(
|
||||
page,
|
||||
duration: const Duration(milliseconds: 150),
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class MobileSortEditorState with _$MobileSortEditorState {
|
||||
factory MobileSortEditorState({
|
||||
required bool showBackButton,
|
||||
required String? editingSortId,
|
||||
required bool isCreatingNewSort,
|
||||
required String? newSortFieldId,
|
||||
required SortConditionPB? newSortCondition,
|
||||
}) = _MobileSortEditorState;
|
||||
|
||||
factory MobileSortEditorState.initial() => MobileSortEditorState(
|
||||
showBackButton: false,
|
||||
editingSortId: null,
|
||||
isCreatingNewSort: false,
|
||||
newSortFieldId: null,
|
||||
newSortCondition: null,
|
||||
);
|
||||
}
|
||||
+35
-42
@@ -3,7 +3,6 @@ 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/widgets/widgets.dart';
|
||||
import 'package:appflowy/plugins/base/drag_handler.dart';
|
||||
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/tab_bar_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||
@@ -27,50 +26,44 @@ class MobileDatabaseViewList extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DraggableScrollableSheet(
|
||||
expand: false,
|
||||
snap: true,
|
||||
initialChildSize: 0.98,
|
||||
minChildSize: 0.98,
|
||||
maxChildSize: 0.98,
|
||||
builder: (context, scrollController) {
|
||||
return BlocBuilder<ViewBloc, ViewState>(
|
||||
builder: (context, state) {
|
||||
final views = [state.view, ...state.view.childViews];
|
||||
return BlocBuilder<ViewBloc, ViewState>(
|
||||
builder: (context, state) {
|
||||
final views = [state.view, ...state.view.childViews];
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
const DragHandler(),
|
||||
_Header(
|
||||
title: LocaleKeys.grid_settings_viewList.plural(
|
||||
context.watch<DatabaseTabBarBloc>().state.tabBars.length,
|
||||
namedArgs: {
|
||||
'count':
|
||||
'${context.watch<DatabaseTabBarBloc>().state.tabBars.length}',
|
||||
},
|
||||
return Column(
|
||||
children: [
|
||||
_Header(
|
||||
title: LocaleKeys.grid_settings_viewList.plural(
|
||||
context.watch<DatabaseTabBarBloc>().state.tabBars.length,
|
||||
namedArgs: {
|
||||
'count':
|
||||
'${context.watch<DatabaseTabBarBloc>().state.tabBars.length}',
|
||||
},
|
||||
),
|
||||
showBackButton: false,
|
||||
useFilledDoneButton: false,
|
||||
onDone: (context) => Navigator.pop(context),
|
||||
),
|
||||
Expanded(
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.zero,
|
||||
children: [
|
||||
...views.mapIndexed(
|
||||
(index, view) => MobileDatabaseViewListButton(
|
||||
view: view,
|
||||
showTopBorder: index == 0,
|
||||
),
|
||||
),
|
||||
showBackButton: false,
|
||||
useFilledDoneButton: false,
|
||||
onDone: (context) => Navigator.pop(context),
|
||||
),
|
||||
SingleChildScrollView(
|
||||
controller: scrollController,
|
||||
child: Column(
|
||||
children: [
|
||||
...views.mapIndexed(
|
||||
(index, view) => MobileDatabaseViewListButton(
|
||||
view: view,
|
||||
showTopBorder: index == 0,
|
||||
),
|
||||
),
|
||||
const VSpace(20),
|
||||
const MobileNewDatabaseViewButton(),
|
||||
],
|
||||
const VSpace(20),
|
||||
const MobileNewDatabaseViewButton(),
|
||||
VSpace(
|
||||
context.bottomSheetPadding(ignoreViewPadding: false),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
+27
-19
@@ -1,6 +1,6 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/show_transition_bottom_sheet.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_quick_action_button.dart';
|
||||
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||
@@ -30,18 +30,13 @@ class MobileDatabaseViewQuickActions extends StatelessWidget {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_actionButton(context, _Action.edit, () {
|
||||
_actionButton(context, _Action.edit, () async {
|
||||
final bloc = context.read<ViewBloc>();
|
||||
context.pop();
|
||||
showMobileBottomSheet(
|
||||
await showTransitionMobileBottomSheet(
|
||||
context,
|
||||
showHeader: true,
|
||||
showDoneButton: true,
|
||||
title: LocaleKeys.grid_settings_editView.tr(),
|
||||
enableDraggableScrollable: true,
|
||||
initialChildSize: 0.98,
|
||||
minChildSize: 0.98,
|
||||
maxChildSize: 0.98,
|
||||
builder: (_) => BlocProvider.value(
|
||||
value: bloc,
|
||||
child: MobileEditDatabaseViewScreen(
|
||||
@@ -49,20 +44,31 @@ class MobileDatabaseViewQuickActions extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
);
|
||||
if (context.mounted) {
|
||||
context.pop();
|
||||
}
|
||||
}),
|
||||
if (!isInline) ...[
|
||||
_divider(),
|
||||
_actionButton(context, _Action.duplicate, () {
|
||||
_divider(),
|
||||
_actionButton(
|
||||
context,
|
||||
_Action.duplicate,
|
||||
() {
|
||||
context.read<ViewBloc>().add(const ViewEvent.duplicate());
|
||||
context.pop();
|
||||
}),
|
||||
_divider(),
|
||||
_actionButton(context, _Action.delete, () {
|
||||
},
|
||||
!isInline,
|
||||
),
|
||||
_divider(),
|
||||
_actionButton(
|
||||
context,
|
||||
_Action.delete,
|
||||
() {
|
||||
context.read<ViewBloc>().add(const ViewEvent.delete());
|
||||
context.pop();
|
||||
}),
|
||||
_divider(),
|
||||
],
|
||||
},
|
||||
!isInline,
|
||||
),
|
||||
_divider(),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -70,14 +76,16 @@ class MobileDatabaseViewQuickActions extends StatelessWidget {
|
||||
Widget _actionButton(
|
||||
BuildContext context,
|
||||
_Action action,
|
||||
VoidCallback onTap,
|
||||
) {
|
||||
VoidCallback onTap, [
|
||||
bool enable = true,
|
||||
]) {
|
||||
return MobileQuickActionButton(
|
||||
icon: action.icon,
|
||||
text: action.label,
|
||||
textColor: action.color(context),
|
||||
iconColor: action.color(context),
|
||||
onTap: onTap,
|
||||
enable: enable,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+13
-20
@@ -3,8 +3,8 @@ import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/flowy_option_tile.dart';
|
||||
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/database_view_service.dart';
|
||||
import 'package:appflowy/plugins/database/application/layout/layout_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/database_view_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/layout_service.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/database_layout_ext.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
@@ -33,8 +33,7 @@ class MobileEditDatabaseViewScreen extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<ViewBloc, ViewState>(
|
||||
builder: (context, state) {
|
||||
return ListView(
|
||||
shrinkWrap: true,
|
||||
return Column(
|
||||
children: [
|
||||
_NameAndIcon(view: state.view),
|
||||
_divider(),
|
||||
@@ -91,13 +90,15 @@ class _NameAndIconState extends State<_NameAndIcon> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FlowyOptionTile.textField(
|
||||
autofocus: true,
|
||||
showTopBorder: false,
|
||||
controller: textEditingController,
|
||||
onTextChanged: (text) {
|
||||
context.read<ViewBloc>().add(ViewEvent.rename(text));
|
||||
},
|
||||
return Material(
|
||||
child: FlowyOptionTile.textField(
|
||||
autofocus: true,
|
||||
showTopBorder: false,
|
||||
controller: textEditingController,
|
||||
onTextChanged: (text) {
|
||||
context.read<ViewBloc>().add(ViewEvent.rename(text));
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -228,19 +229,12 @@ class DatabaseViewSettingTile extends StatelessWidget {
|
||||
}
|
||||
|
||||
if (setting == DatabaseViewSettings.fields) {
|
||||
await showMobileBottomSheet(
|
||||
await showTransitionMobileBottomSheet(
|
||||
context,
|
||||
useSafeArea: false,
|
||||
resizeToAvoidBottomInset: false,
|
||||
showDragHandle: true,
|
||||
showHeader: true,
|
||||
showBackButton: true,
|
||||
title: LocaleKeys.grid_settings_properties.tr(),
|
||||
showDivider: true,
|
||||
enableDraggableScrollable: true,
|
||||
initialChildSize: 0.98,
|
||||
minChildSize: 0.98,
|
||||
maxChildSize: 0.98,
|
||||
builder: (_) {
|
||||
return BlocProvider.value(
|
||||
value: context.read<ViewBloc>(),
|
||||
@@ -257,7 +251,6 @@ class DatabaseViewSettingTile extends StatelessWidget {
|
||||
if (setting == DatabaseViewSettings.board) {
|
||||
await showMobileBottomSheet<DatabaseLayoutPB>(
|
||||
context,
|
||||
resizeToAvoidBottomInset: false,
|
||||
builder: (context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 24, bottom: 46),
|
||||
|
||||
+6
-4
@@ -35,10 +35,12 @@ class MobileFavoriteScreen extends StatelessWidget {
|
||||
},
|
||||
(error) => null,
|
||||
);
|
||||
final userProfile =
|
||||
snapshots.data?[1].fold((error) => null, (userProfilePB) {
|
||||
return userProfilePB as UserProfilePB?;
|
||||
});
|
||||
final userProfile = snapshots.data?[1].fold(
|
||||
(userProfilePB) {
|
||||
return userProfilePB as UserProfilePB?;
|
||||
},
|
||||
(error) => null,
|
||||
);
|
||||
|
||||
// In the unlikely case either of the above is null, eg.
|
||||
// when a workspace is already open this can happen.
|
||||
|
||||
@@ -41,10 +41,12 @@ class MobileHomeScreen extends StatelessWidget {
|
||||
},
|
||||
(error) => null,
|
||||
);
|
||||
final userProfile =
|
||||
snapshots.data?[1].fold((error) => null, (userProfilePB) {
|
||||
return userProfilePB as UserProfilePB?;
|
||||
});
|
||||
final userProfile = snapshots.data?[1].fold(
|
||||
(userProfilePB) {
|
||||
return userProfilePB as UserProfilePB?;
|
||||
},
|
||||
(error) => null,
|
||||
);
|
||||
|
||||
// In the unlikely case either of the above is null, eg.
|
||||
// when a workspace is already open this can happen.
|
||||
|
||||
+9
-6
@@ -35,12 +35,15 @@ class _MobileHomeSettingPageState extends State<MobileHomeSettingPage> {
|
||||
return const Center(child: CircularProgressIndicator.adaptive());
|
||||
}
|
||||
|
||||
final userProfile = snapshot.data?.fold((error) {
|
||||
errorMsg = error.msg;
|
||||
return null;
|
||||
}, (userProfile) {
|
||||
return userProfile;
|
||||
});
|
||||
final userProfile = snapshot.data?.fold(
|
||||
(userProfile) {
|
||||
return userProfile;
|
||||
},
|
||||
(error) {
|
||||
errorMsg = error.msg;
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
return Scaffold(
|
||||
appBar: FlowyAppBar(
|
||||
|
||||
+2
-2
@@ -22,14 +22,14 @@ class AboutSettingGroup extends StatelessWidget {
|
||||
trailing: const Icon(
|
||||
Icons.chevron_right,
|
||||
),
|
||||
onTap: () => safeLaunchUrl('https://appflowy.io/privacy/mobile'),
|
||||
onTap: () => safeLaunchUrl('https://appflowy.io/privacy/app'),
|
||||
),
|
||||
MobileSettingItem(
|
||||
name: LocaleKeys.settings_mobile_termsAndConditions.tr(),
|
||||
trailing: const Icon(
|
||||
Icons.chevron_right,
|
||||
),
|
||||
onTap: () => safeLaunchUrl('https://appflowy.io/terms'),
|
||||
onTap: () => safeLaunchUrl('https://appflowy.io/terms/app'),
|
||||
),
|
||||
MobileSettingItem(
|
||||
name: LocaleKeys.settings_mobile_version.tr(),
|
||||
|
||||
+2
@@ -1,5 +1,6 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/setting/appearance/rtl_setting.dart';
|
||||
import 'package:appflowy/mobile/presentation/setting/appearance/text_scale_setting.dart';
|
||||
import 'package:appflowy/mobile/presentation/setting/appearance/theme_setting.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -18,6 +19,7 @@ class AppearanceSettingGroup extends StatelessWidget {
|
||||
settingItemList: const [
|
||||
ThemeSetting(),
|
||||
FontSetting(),
|
||||
TextScaleSetting(),
|
||||
RTLSetting(),
|
||||
],
|
||||
);
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/more_view_actions/widgets/font_size_stepper.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../setting.dart';
|
||||
|
||||
const int _divisions = 4;
|
||||
|
||||
class TextScaleSetting extends StatelessWidget {
|
||||
const TextScaleSetting({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final textScaleFactor =
|
||||
context.watch<AppearanceSettingsCubit>().state.textScaleFactor;
|
||||
return MobileSettingItem(
|
||||
name: LocaleKeys.settings_appearance_fontScaleFactor.tr(),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
FlowyText(
|
||||
// map the text scale factor to the 0-1
|
||||
// 0.8 - 0.0
|
||||
// 0.9 - 0.5
|
||||
// 1.0 - 1.0
|
||||
((_divisions + 1) * textScaleFactor - _divisions)
|
||||
.toStringAsFixed(2),
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
const Icon(Icons.chevron_right),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
showMobileBottomSheet(
|
||||
context,
|
||||
showHeader: true,
|
||||
showDragHandle: true,
|
||||
showDivider: false,
|
||||
showCloseButton: false,
|
||||
title: LocaleKeys.settings_appearance_fontScaleFactor.tr(),
|
||||
builder: (context) {
|
||||
return FontSizeStepper(
|
||||
value: textScaleFactor,
|
||||
minimumValue: 0.8,
|
||||
maximumValue: 1.0,
|
||||
divisions: _divisions,
|
||||
onChanged: (newTextScaleFactor) {
|
||||
context
|
||||
.read<AppearanceSettingsCubit>()
|
||||
.setTextScaleFactor(newTextScaleFactor);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/setting/font/font_picker_screen.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
|
||||
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
|
||||
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.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:go_router/go_router.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ class _LanguageSettingGroupState extends State<LanguageSettingGroup> {
|
||||
final newLocale =
|
||||
await context.push<Locale>(LanguagePickerScreen.routeName);
|
||||
if (newLocale != null && newLocale != locale) {
|
||||
if (mounted) {
|
||||
if (context.mounted) {
|
||||
context
|
||||
.read<AppearanceSettingsCubit>()
|
||||
.setLocale(context, newLocale);
|
||||
|
||||
+4
-6
@@ -5,6 +5,7 @@ import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||
import 'package:appflowy/user/application/sign_in_bloc.dart';
|
||||
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/sign_in_or_logout_button.dart';
|
||||
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -27,12 +28,9 @@ class UserSessionSettingGroup extends StatelessWidget {
|
||||
create: (context) => getIt<SignInBloc>(),
|
||||
child: BlocConsumer<SignInBloc, SignInState>(
|
||||
listener: (context, state) {
|
||||
state.successOrFail.fold(
|
||||
() => null,
|
||||
(result) => result.fold(
|
||||
(l) {},
|
||||
(r) async => runAppFlowy(),
|
||||
),
|
||||
state.successOrFail?.fold(
|
||||
(result) => runAppFlowy(),
|
||||
(e) => Log.error(e),
|
||||
);
|
||||
},
|
||||
builder: (context, state) {
|
||||
|
||||
+11
-3
@@ -10,6 +10,7 @@ class MobileQuickActionButton extends StatelessWidget {
|
||||
required this.text,
|
||||
this.textColor,
|
||||
this.iconColor,
|
||||
this.enable = true,
|
||||
});
|
||||
|
||||
final VoidCallback onTap;
|
||||
@@ -17,14 +18,21 @@ class MobileQuickActionButton extends StatelessWidget {
|
||||
final String text;
|
||||
final Color? textColor;
|
||||
final Color? iconColor;
|
||||
final bool enable;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
onTap: () {
|
||||
if (enable) {
|
||||
onTap();
|
||||
}
|
||||
},
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
overlayColor:
|
||||
enable ? null : const MaterialStatePropertyAll(Colors.transparent),
|
||||
splashColor: Colors.transparent,
|
||||
child: Container(
|
||||
height: 44,
|
||||
@@ -34,14 +42,14 @@ class MobileQuickActionButton extends StatelessWidget {
|
||||
FlowySvg(
|
||||
icon,
|
||||
size: const Size.square(20),
|
||||
color: iconColor,
|
||||
color: enable ? iconColor : Theme.of(context).disabledColor,
|
||||
),
|
||||
const HSpace(12),
|
||||
Expanded(
|
||||
child: FlowyText(
|
||||
text,
|
||||
fontSize: 15,
|
||||
color: textColor,
|
||||
color: enable ? textColor : Theme.of(context).disabledColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -218,7 +218,7 @@ class FlowyOptionTile extends StatelessWidget {
|
||||
|
||||
final padding = EdgeInsets.symmetric(
|
||||
horizontal: leading == null ? 0.0 : 12.0,
|
||||
vertical: 16.0,
|
||||
vertical: 14.0,
|
||||
);
|
||||
|
||||
return Expanded(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DragHandler extends StatelessWidget {
|
||||
const DragHandler({
|
||||
class DragHandle extends StatelessWidget {
|
||||
const DragHandle({
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
||||
+5
-5
@@ -4,11 +4,11 @@ import 'dart:typed_data';
|
||||
import 'package:appflowy/core/notification/grid_notification.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
|
||||
typedef UpdateCalculationValue
|
||||
= Either<CalculationChangesetNotificationPB, FlowyError>;
|
||||
= FlowyResult<CalculationChangesetNotificationPB, FlowyError>;
|
||||
|
||||
class CalculationsListener {
|
||||
CalculationsListener({required this.viewId});
|
||||
@@ -31,15 +31,15 @@ class CalculationsListener {
|
||||
|
||||
void _handler(
|
||||
DatabaseNotification ty,
|
||||
Either<Uint8List, FlowyError> result,
|
||||
FlowyResult<Uint8List, FlowyError> result,
|
||||
) {
|
||||
switch (ty) {
|
||||
case DatabaseNotification.DidUpdateCalculation:
|
||||
_calculationNotifier?.value = result.fold(
|
||||
(payload) => left(
|
||||
(payload) => FlowyResult.success(
|
||||
CalculationChangesetNotificationPB.fromBuffer(payload),
|
||||
),
|
||||
(err) => right(err),
|
||||
(err) => FlowyResult.failure(err),
|
||||
);
|
||||
default:
|
||||
break;
|
||||
|
||||
+4
-2
@@ -1,7 +1,7 @@
|
||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
|
||||
class CalculationsBackendService {
|
||||
const CalculationsBackendService({required this.viewId});
|
||||
@@ -9,7 +9,9 @@ class CalculationsBackendService {
|
||||
final String viewId;
|
||||
|
||||
// Get Calculations (initial fetch)
|
||||
Future<Either<RepeatedCalculationsPB, FlowyError>> getCalculations() async {
|
||||
|
||||
Future<FlowyResult<RepeatedCalculationsPB, FlowyError>>
|
||||
getCalculations() async {
|
||||
final payload = DatabaseViewIdPB()..value = viewId;
|
||||
|
||||
return DatabaseEventGetAllCalculations(payload).send();
|
||||
|
||||
+3
-3
@@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
@@ -86,7 +87,6 @@ class CheckboxCellState with _$CheckboxCellState {
|
||||
}
|
||||
}
|
||||
|
||||
bool _isSelected(String? cellData) {
|
||||
// The backend use "Yes" and "No" to represent the checkbox cell data.
|
||||
return cellData == "Yes";
|
||||
bool _isSelected(CheckboxCellDataPB? cellData) {
|
||||
return cellData != null && cellData.isChecked;
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/checklist_cell_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/checklist_cell_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/checklist_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/select_option_entities.pb.dart';
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@ import 'dart:async';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/date_cell_service.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/date_cell_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_service.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/type_option/type_option_data_parser.dart';
|
||||
import 'package:appflowy/user/application/reminder/reminder_bloc.dart';
|
||||
import 'package:appflowy/user/application/reminder/reminder_extension.dart';
|
||||
|
||||
+26
-30
@@ -1,12 +1,10 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/select_option_cell_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/select_option_cell_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/select_option_entities.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
@@ -52,7 +50,7 @@ class SelectOptionCellEditorBloc
|
||||
await _createOption(optionName);
|
||||
emit(
|
||||
state.copyWith(
|
||||
filter: none(),
|
||||
filter: null,
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -164,7 +162,7 @@ class SelectOptionCellEditorBloc
|
||||
}
|
||||
|
||||
// clear the filter
|
||||
emit(state.copyWith(filter: none()));
|
||||
emit(state.copyWith(filter: null));
|
||||
}
|
||||
|
||||
void _selectMultipleOptions(List<String> optionNames) {
|
||||
@@ -186,12 +184,12 @@ class SelectOptionCellEditorBloc
|
||||
|
||||
void _filterOption(String optionName, Emitter<SelectOptionEditorState> emit) {
|
||||
final _MakeOptionResult result = _makeOptions(
|
||||
Some(optionName),
|
||||
optionName,
|
||||
state.allOptions,
|
||||
);
|
||||
emit(
|
||||
state.copyWith(
|
||||
filter: Some(optionName),
|
||||
filter: optionName,
|
||||
options: result.options,
|
||||
createOption: result.createOption,
|
||||
),
|
||||
@@ -201,7 +199,7 @@ class SelectOptionCellEditorBloc
|
||||
Future<void> _loadOptions() async {
|
||||
final result = await _selectOptionService.getCellData();
|
||||
if (isClosed) {
|
||||
Log.warn("Unexpected closing the bloc");
|
||||
Log.warn("Unexpecteded closing the bloc");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -220,28 +218,26 @@ class SelectOptionCellEditorBloc
|
||||
}
|
||||
|
||||
_MakeOptionResult _makeOptions(
|
||||
Option<String> filter,
|
||||
String? filter,
|
||||
List<SelectOptionPB> allOptions,
|
||||
) {
|
||||
final List<SelectOptionPB> options = List.from(allOptions);
|
||||
Option<String> createOption = filter;
|
||||
String? createOption = filter;
|
||||
|
||||
filter.foldRight(null, (filter, previous) {
|
||||
if (filter.isNotEmpty) {
|
||||
options.retainWhere((option) {
|
||||
final name = option.name.toLowerCase();
|
||||
final lFilter = filter.toLowerCase();
|
||||
if (filter != null && filter.isNotEmpty) {
|
||||
options.retainWhere((option) {
|
||||
final name = option.name.toLowerCase();
|
||||
final lFilter = filter.toLowerCase();
|
||||
|
||||
if (name == lFilter) {
|
||||
createOption = none();
|
||||
}
|
||||
if (name == lFilter) {
|
||||
createOption = null;
|
||||
}
|
||||
|
||||
return name.contains(lFilter);
|
||||
});
|
||||
} else {
|
||||
createOption = none();
|
||||
}
|
||||
});
|
||||
return name.contains(lFilter);
|
||||
});
|
||||
} else {
|
||||
createOption = null;
|
||||
}
|
||||
|
||||
return _MakeOptionResult(
|
||||
options: options,
|
||||
@@ -295,8 +291,8 @@ class SelectOptionEditorState with _$SelectOptionEditorState {
|
||||
required List<SelectOptionPB> options,
|
||||
required List<SelectOptionPB> allOptions,
|
||||
required List<SelectOptionPB> selectedOptions,
|
||||
required Option<String> createOption,
|
||||
required Option<String> filter,
|
||||
required String? createOption,
|
||||
required String? filter,
|
||||
}) = _SelectOptionEditorState;
|
||||
|
||||
factory SelectOptionEditorState.initial(SelectOptionCellController context) {
|
||||
@@ -305,8 +301,8 @@ class SelectOptionEditorState with _$SelectOptionEditorState {
|
||||
options: data?.options ?? [],
|
||||
allOptions: data?.options ?? [],
|
||||
selectedOptions: data?.selectOptions ?? [],
|
||||
createOption: none(),
|
||||
filter: none(),
|
||||
createOption: null,
|
||||
filter: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -318,5 +314,5 @@ class _MakeOptionResult {
|
||||
});
|
||||
|
||||
List<SelectOptionPB> options;
|
||||
Option<String> createOption;
|
||||
String? createOption;
|
||||
}
|
||||
+4
-5
@@ -2,22 +2,21 @@ import 'dart:async';
|
||||
|
||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_info.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_listener.dart';
|
||||
import 'package:appflowy/plugins/database/domain/cell_listener.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_listener.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/type_option/type_option_data_parser.dart';
|
||||
import 'package:appflowy/plugins/database/application/row/row_cache.dart';
|
||||
import 'package:appflowy/plugins/database/application/row/row_meta_listener.dart';
|
||||
import 'package:appflowy/plugins/database/domain/row_meta_listener.dart';
|
||||
import 'package:appflowy/plugins/database/application/row/row_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import 'cell_cache.dart';
|
||||
import 'cell_data_loader.dart';
|
||||
import 'cell_data_persistence.dart';
|
||||
import 'cell_listener.dart';
|
||||
|
||||
part 'cell_controller.freezed.dart';
|
||||
|
||||
@@ -173,7 +172,7 @@ class CellController<T, D> {
|
||||
Future<void> saveCellData(
|
||||
D data, {
|
||||
bool debounce = false,
|
||||
void Function(Option<FlowyError>)? onFinish,
|
||||
void Function(FlowyError?)? onFinish,
|
||||
}) async {
|
||||
_loadDataOperation?.cancel();
|
||||
if (debounce) {
|
||||
|
||||
+3
-3
@@ -6,7 +6,7 @@ import 'cell_data_loader.dart';
|
||||
import 'cell_data_persistence.dart';
|
||||
|
||||
typedef TextCellController = CellController<String, String>;
|
||||
typedef CheckboxCellController = CellController<String, String>;
|
||||
typedef CheckboxCellController = CellController<CheckboxCellDataPB, String>;
|
||||
typedef NumberCellController = CellController<String, String>;
|
||||
typedef SelectOptionCellController
|
||||
= CellController<SelectOptionCellDataPB, String>;
|
||||
@@ -24,13 +24,13 @@ CellController makeCellController(
|
||||
final fieldType = fieldController.getField(cellContext.fieldId)!.fieldType;
|
||||
switch (fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
return TextCellController(
|
||||
return CheckboxCellController(
|
||||
viewId: viewId,
|
||||
fieldController: fieldController,
|
||||
cellContext: cellContext,
|
||||
rowCache: rowCache,
|
||||
cellDataLoader: CellDataLoader(
|
||||
parser: StringCellDataParser(),
|
||||
parser: CheckboxCellDataParser(),
|
||||
),
|
||||
cellDataPersistence: TextCellDataPersistence(),
|
||||
);
|
||||
|
||||
+11
-1
@@ -1,10 +1,10 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:appflowy/plugins/database/domain/cell_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
|
||||
import 'cell_controller.dart';
|
||||
import 'cell_service.dart';
|
||||
|
||||
abstract class IGridCellDataConfig {
|
||||
// The cell data will reload if it receives the field's change notification.
|
||||
@@ -66,6 +66,16 @@ class StringCellDataParser implements CellDataParser<String> {
|
||||
}
|
||||
}
|
||||
|
||||
class CheckboxCellDataParser implements CellDataParser<CheckboxCellDataPB> {
|
||||
@override
|
||||
CheckboxCellDataPB? parserData(List<int> data) {
|
||||
if (data.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return CheckboxCellDataPB.fromBuffer(data);
|
||||
}
|
||||
}
|
||||
|
||||
class NumberCellDataParser implements CellDataParser<String> {
|
||||
@override
|
||||
String? parserData(List<int> data) {
|
||||
|
||||
+5
-6
@@ -1,13 +1,12 @@
|
||||
import 'package:appflowy/plugins/database/domain/cell_service.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/protobuf.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import 'cell_controller.dart';
|
||||
import 'cell_service.dart';
|
||||
|
||||
/// Save the cell data to disk
|
||||
/// You can extend this class to do custom operations.
|
||||
abstract class CellDataPersistence<D> {
|
||||
Future<Option<FlowyError>> save({
|
||||
Future<FlowyError?> save({
|
||||
required String viewId,
|
||||
required CellContext cellContext,
|
||||
required D data,
|
||||
@@ -18,7 +17,7 @@ class TextCellDataPersistence implements CellDataPersistence<String> {
|
||||
TextCellDataPersistence();
|
||||
|
||||
@override
|
||||
Future<Option<FlowyError>> save({
|
||||
Future<FlowyError?> save({
|
||||
required String viewId,
|
||||
required CellContext cellContext,
|
||||
required String data,
|
||||
@@ -30,8 +29,8 @@ class TextCellDataPersistence implements CellDataPersistence<String> {
|
||||
);
|
||||
return fut.then((result) {
|
||||
return result.fold(
|
||||
(l) => none(),
|
||||
(err) => Some(err),
|
||||
(l) => null,
|
||||
(err) => err,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
+15
-20
@@ -1,26 +1,21 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/view/view_cache.dart';
|
||||
import 'package:appflowy/plugins/database/domain/database_view_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/group_listener.dart';
|
||||
import 'package:appflowy/plugins/database/domain/layout_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/layout_setting_listener.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/board_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/calendar_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/group.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/group_changeset.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/row_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'dart:async';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'database_view_service.dart';
|
||||
import 'defines.dart';
|
||||
import 'layout/layout_service.dart';
|
||||
import 'layout/layout_setting_listener.dart';
|
||||
import 'row/row_cache.dart';
|
||||
import 'group/group_listener.dart';
|
||||
|
||||
typedef OnGroupConfigurationChanged = void Function(List<GroupSettingPB>);
|
||||
typedef OnGroupByField = void Function(List<GroupPB>);
|
||||
@@ -136,7 +131,7 @@ class DatabaseController {
|
||||
}
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> open() async {
|
||||
Future<FlowyResult<void, FlowyError>> open() async {
|
||||
return _databaseViewBackendSvc.openDatabase().then((result) {
|
||||
return result.fold(
|
||||
(DatabasePB database) async {
|
||||
@@ -157,21 +152,21 @@ class DatabaseController {
|
||||
return Future(() async {
|
||||
await _loadGroups();
|
||||
await _loadLayoutSetting();
|
||||
return left(fields);
|
||||
return FlowyResult.success(fields);
|
||||
});
|
||||
},
|
||||
(err) {
|
||||
Log.error(err);
|
||||
return right(err);
|
||||
return FlowyResult.failure(err);
|
||||
},
|
||||
);
|
||||
},
|
||||
(err) => right(err),
|
||||
(err) => FlowyResult.failure(err),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> moveGroupRow({
|
||||
Future<FlowyResult<void, FlowyError>> moveGroupRow({
|
||||
required RowMetaPB fromRow,
|
||||
required String fromGroupId,
|
||||
required String toGroupId,
|
||||
@@ -185,7 +180,7 @@ class DatabaseController {
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> moveRow({
|
||||
Future<FlowyResult<void, FlowyError>> moveRow({
|
||||
required String fromRowId,
|
||||
required String toRowId,
|
||||
}) {
|
||||
@@ -195,7 +190,7 @@ class DatabaseController {
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> moveGroup({
|
||||
Future<FlowyResult<void, FlowyError>> moveGroup({
|
||||
required String fromGroupId,
|
||||
required String toGroupId,
|
||||
}) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/filte
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/sort/sort_info.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import 'field/field_info.dart';
|
||||
@@ -38,7 +38,7 @@ class LoadingState with _$LoadingState {
|
||||
const factory LoadingState.idle() = _Idle;
|
||||
const factory LoadingState.loading() = _Loading;
|
||||
const factory LoadingState.finish(
|
||||
Either<Unit, FlowyError> successOrFail,
|
||||
FlowyResult<void, FlowyError> successOrFail,
|
||||
) = _Finish;
|
||||
|
||||
const LoadingState._();
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:appflowy/plugins/database/application/field_settings/field_settings_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_settings_service.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
|
||||
+63
-38
@@ -1,30 +1,26 @@
|
||||
import 'package:appflowy/plugins/database/application/database_view_service.dart';
|
||||
import 'package:appflowy/plugins/database/application/field_settings/field_settings_listener.dart';
|
||||
import 'package:appflowy/plugins/database/application/field_settings/field_settings_service.dart';
|
||||
import 'package:appflowy/plugins/database/application/filter/filter_listener.dart';
|
||||
import 'package:appflowy/plugins/database/application/filter/filter_service.dart';
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:appflowy/plugins/database/domain/database_view_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_listener.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_settings_listener.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_settings_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/filter_listener.dart';
|
||||
import 'package:appflowy/plugins/database/domain/filter_service.dart';
|
||||
import 'package:appflowy/plugins/database/application/row/row_cache.dart';
|
||||
import 'package:appflowy/plugins/database/application/setting/setting_listener.dart';
|
||||
import 'package:appflowy/plugins/database/application/sort/sort_listener.dart';
|
||||
import 'package:appflowy/plugins/database/application/sort/sort_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/sort_listener.dart';
|
||||
import 'package:appflowy/plugins/database/domain/sort_service.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/filter_info.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/sort/sort_info.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_settings_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/filter_changeset.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/group.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/sort_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/util.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import '../setting/setting_service.dart';
|
||||
import 'field_info.dart';
|
||||
import 'field_listener.dart';
|
||||
|
||||
class _GridFieldNotifier extends ChangeNotifier {
|
||||
List<FieldInfo> _fieldInfos = [];
|
||||
@@ -287,7 +283,7 @@ class FieldController {
|
||||
final fieldInfo = _findFieldInfo(
|
||||
fieldInfos: fieldInfos,
|
||||
fieldId: newSortPB.sort.fieldId,
|
||||
fieldType: newSortPB.sort.fieldType,
|
||||
fieldType: null,
|
||||
);
|
||||
|
||||
if (fieldInfo != null) {
|
||||
@@ -316,7 +312,7 @@ class FieldController {
|
||||
final fieldInfo = _findFieldInfo(
|
||||
fieldInfos: fieldInfos,
|
||||
fieldId: updatedSort.fieldId,
|
||||
fieldType: updatedSort.fieldType,
|
||||
fieldType: null,
|
||||
);
|
||||
|
||||
if (fieldInfo != null) {
|
||||
@@ -333,6 +329,32 @@ class FieldController {
|
||||
}
|
||||
}
|
||||
|
||||
void updateFieldInfos(
|
||||
List<SortInfo> newSortInfos,
|
||||
SortChangesetNotificationPB changeset,
|
||||
) {
|
||||
final changedFieldIds = HashSet<String>.from([
|
||||
...changeset.insertSorts.map((sort) => sort.sort.fieldId),
|
||||
...changeset.updateSorts.map((sort) => sort.fieldId),
|
||||
...changeset.deleteSorts.map((sort) => sort.fieldId),
|
||||
]);
|
||||
|
||||
final newFieldInfos = [...fieldInfos];
|
||||
|
||||
for (final fieldId in changedFieldIds) {
|
||||
final index =
|
||||
newFieldInfos.indexWhere((fieldInfo) => fieldInfo.id == fieldId);
|
||||
if (index == -1) {
|
||||
continue;
|
||||
}
|
||||
newFieldInfos[index] = newFieldInfos[index].copyWith(
|
||||
hasSort: newSortInfos.any((sort) => sort.fieldId == fieldId),
|
||||
);
|
||||
}
|
||||
|
||||
_fieldNotifier.fieldInfos = newFieldInfos;
|
||||
}
|
||||
|
||||
_sortsListener.start(
|
||||
onSortChanged: (result) {
|
||||
if (_isDisposed) {
|
||||
@@ -346,6 +368,7 @@ class FieldController {
|
||||
updateSortFromChangeset(newSortInfos, changeset);
|
||||
|
||||
_sortNotifier?.sorts = newSortInfos;
|
||||
updateFieldInfos(newSortInfos, changeset);
|
||||
},
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
@@ -353,7 +376,7 @@ class FieldController {
|
||||
);
|
||||
}
|
||||
|
||||
/// Listen for databse setting changes in the backend.
|
||||
/// Listen for database setting changes in the backend.
|
||||
void _listenOnSettingChanges() {
|
||||
_settingListener.start(
|
||||
onSettingUpdated: (result) {
|
||||
@@ -552,7 +575,7 @@ class FieldController {
|
||||
}
|
||||
|
||||
/// Load all of the fields. This is required when opening the database
|
||||
Future<Either<Unit, FlowyError>> loadFields({
|
||||
Future<FlowyResult<void, FlowyError>> loadFields({
|
||||
required List<FieldIdPB> fieldIds,
|
||||
}) async {
|
||||
final result = await _databaseViewBackendSvc.getFields(fieldIds: fieldIds);
|
||||
@@ -560,7 +583,7 @@ class FieldController {
|
||||
() => result.fold(
|
||||
(newFields) async {
|
||||
if (_isDisposed) {
|
||||
return left(unit);
|
||||
return FlowyResult.success(null);
|
||||
}
|
||||
|
||||
_fieldNotifier.fieldInfos =
|
||||
@@ -573,54 +596,54 @@ class FieldController {
|
||||
]);
|
||||
_updateFieldInfos();
|
||||
|
||||
return left(unit);
|
||||
return FlowyResult.success(null);
|
||||
},
|
||||
(err) => right(err),
|
||||
(err) => FlowyResult.failure(err),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Load all the filters from the backend. Required by `loadFields`
|
||||
Future<Either<Unit, FlowyError>> _loadFilters() async {
|
||||
Future<FlowyResult<void, FlowyError>> _loadFilters() async {
|
||||
return _filterBackendSvc.getAllFilters().then((result) {
|
||||
return result.fold(
|
||||
(filterPBs) {
|
||||
_filterNotifier?.filters = _filterInfoListFromPBs(filterPBs);
|
||||
return left(unit);
|
||||
return FlowyResult.success(null);
|
||||
},
|
||||
(err) => right(err),
|
||||
(err) => FlowyResult.failure(err),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Load all the sorts from the backend. Required by `loadFields`
|
||||
Future<Either<Unit, FlowyError>> _loadSorts() async {
|
||||
Future<FlowyResult<void, FlowyError>> _loadSorts() async {
|
||||
return _sortBackendSvc.getAllSorts().then((result) {
|
||||
return result.fold(
|
||||
(sortPBs) {
|
||||
_sortNotifier?.sorts = _sortInfoListFromPBs(sortPBs);
|
||||
return left(unit);
|
||||
return FlowyResult.success(null);
|
||||
},
|
||||
(err) => right(err),
|
||||
(err) => FlowyResult.failure(err),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Load all the field settings from the backend. Required by `loadFields`
|
||||
Future<Either<Unit, FlowyError>> _loadAllFieldSettings() async {
|
||||
Future<FlowyResult<void, FlowyError>> _loadAllFieldSettings() async {
|
||||
return _fieldSettingsBackendSvc.getAllFieldSettings().then((result) {
|
||||
return result.fold(
|
||||
(fieldSettingsList) {
|
||||
_fieldSettings.clear();
|
||||
_fieldSettings.addAll(fieldSettingsList);
|
||||
return left(unit);
|
||||
return FlowyResult.success(null);
|
||||
},
|
||||
(err) => right(err),
|
||||
(err) => FlowyResult.failure(err),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> _loadSettings() async {
|
||||
Future<FlowyResult<void, FlowyError>> _loadSettings() async {
|
||||
return SettingBackendService(viewId: viewId).getSetting().then(
|
||||
(result) => result.fold(
|
||||
(setting) {
|
||||
@@ -629,9 +652,9 @@ class FieldController {
|
||||
_groupConfigurationByFieldId[configuration.fieldId] =
|
||||
configuration;
|
||||
}
|
||||
return left(unit);
|
||||
return FlowyResult.success(null);
|
||||
},
|
||||
(err) => right(err),
|
||||
(err) => FlowyResult.failure(err),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -659,7 +682,7 @@ class FieldController {
|
||||
final fieldInfo = _findFieldInfo(
|
||||
fieldInfos: fieldInfos,
|
||||
fieldId: sortPB.fieldId,
|
||||
fieldType: sortPB.fieldType,
|
||||
fieldType: null,
|
||||
);
|
||||
return fieldInfo != null
|
||||
? SortInfo(sortPB: sortPB, fieldInfo: fieldInfo)
|
||||
@@ -818,9 +841,11 @@ class RowCacheDependenciesImpl extends RowFieldsDelegate with RowLifeCycle {
|
||||
FieldInfo? _findFieldInfo({
|
||||
required List<FieldInfo> fieldInfos,
|
||||
required String fieldId,
|
||||
required FieldType fieldType,
|
||||
required FieldType? fieldType,
|
||||
}) {
|
||||
return fieldInfos.firstWhereOrNull(
|
||||
(element) => element.id == fieldId && element.fieldType == fieldType,
|
||||
(element) =>
|
||||
element.id == fieldId &&
|
||||
(fieldType == null || element.fieldType == fieldType),
|
||||
);
|
||||
}
|
||||
+5
-5
@@ -1,17 +1,17 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:appflowy/plugins/database/application/field_settings/field_settings_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_listener.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_settings_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import 'field_controller.dart';
|
||||
import 'field_info.dart';
|
||||
import 'field_listener.dart';
|
||||
import 'field_service.dart';
|
||||
|
||||
part 'field_editor_bloc.freezed.dart';
|
||||
|
||||
@@ -111,7 +111,7 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
);
|
||||
}
|
||||
|
||||
void _logIfError(Either<Unit, FlowyError> result) {
|
||||
void _logIfError(FlowyResult<void, FlowyError> result) {
|
||||
result.fold(
|
||||
(l) => null,
|
||||
(err) => Log.error(err),
|
||||
|
||||
@@ -47,9 +47,12 @@ class FieldInfo with _$FieldInfo {
|
||||
}
|
||||
|
||||
bool get canCreateFilter {
|
||||
if (hasFilter) return false;
|
||||
if (hasFilter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (field.fieldType) {
|
||||
case FieldType.Number:
|
||||
case FieldType.Checkbox:
|
||||
case FieldType.MultiSelect:
|
||||
case FieldType.RichText:
|
||||
@@ -62,7 +65,9 @@ class FieldInfo with _$FieldInfo {
|
||||
}
|
||||
|
||||
bool get canCreateSort {
|
||||
if (hasSort) return false;
|
||||
if (hasSort) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (field.fieldType) {
|
||||
case FieldType.RichText:
|
||||
@@ -73,6 +78,7 @@ class FieldInfo with _$FieldInfo {
|
||||
case FieldType.MultiSelect:
|
||||
case FieldType.LastEditedTime:
|
||||
case FieldType.CreatedTime:
|
||||
case FieldType.Checklist:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
||||
+4
-5
@@ -1,5 +1,4 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/select_option_entities.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
@@ -28,10 +27,10 @@ class SelectOptionTypeOptionBloc
|
||||
emit(state.copyWith(options: options));
|
||||
},
|
||||
addingOption: () {
|
||||
emit(state.copyWith(isEditingOption: true, newOptionName: none()));
|
||||
emit(state.copyWith(isEditingOption: true, newOptionName: null));
|
||||
},
|
||||
endAddingOption: () {
|
||||
emit(state.copyWith(isEditingOption: false, newOptionName: none()));
|
||||
emit(state.copyWith(isEditingOption: false, newOptionName: null));
|
||||
},
|
||||
updateOption: (option) {
|
||||
final List<SelectOptionPB> options =
|
||||
@@ -69,13 +68,13 @@ class SelectOptionTypeOptionState with _$SelectOptionTypeOptionState {
|
||||
const factory SelectOptionTypeOptionState({
|
||||
required List<SelectOptionPB> options,
|
||||
required bool isEditingOption,
|
||||
required Option<String> newOptionName,
|
||||
required String? newOptionName,
|
||||
}) = _SelectOptionTypeOptionState;
|
||||
|
||||
factory SelectOptionTypeOptionState.initial(List<SelectOptionPB> options) =>
|
||||
SelectOptionTypeOptionState(
|
||||
options: options,
|
||||
isEditingOption: false,
|
||||
newOptionName: none(),
|
||||
newOptionName: null,
|
||||
);
|
||||
}
|
||||
+1
-2
@@ -1,11 +1,10 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:appflowy/plugins/database/domain/type_option_service.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/type_option/builder.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/select_option_entities.pb.dart';
|
||||
|
||||
import 'type_option_service.dart';
|
||||
|
||||
abstract class ISelectOptionAction {
|
||||
ISelectOptionAction({
|
||||
required this.onTypeOptionUpdated,
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import 'package:appflowy/plugins/database/domain/database_view_service.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../database_view_service.dart';
|
||||
|
||||
part 'layout_bloc.freezed.dart';
|
||||
|
||||
class DatabaseLayoutBloc
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import 'package:appflowy/plugins/database/application/field/field_listener.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_listener.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_service.dart';
|
||||
import 'package:appflowy/plugins/database/application/row/row_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import 'row_meta_listener.dart';
|
||||
import '../../domain/row_meta_listener.dart';
|
||||
|
||||
part 'row_banner_bloc.freezed.dart';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
|
||||
import '../field/field_info.dart';
|
||||
|
||||
@@ -12,7 +12,7 @@ class RowBackendService {
|
||||
|
||||
final String viewId;
|
||||
|
||||
static Future<Either<RowMetaPB, FlowyError>> createRow({
|
||||
static Future<FlowyResult<RowMetaPB, FlowyError>> createRow({
|
||||
required String viewId,
|
||||
String? groupId,
|
||||
void Function(RowDataBuilder builder)? withCells,
|
||||
@@ -43,7 +43,7 @@ class RowBackendService {
|
||||
return DatabaseEventCreateRow(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<RowMetaPB, FlowyError>> createRowBefore(RowId rowId) {
|
||||
Future<FlowyResult<RowMetaPB, FlowyError>> createRowBefore(RowId rowId) {
|
||||
return createRow(
|
||||
viewId: viewId,
|
||||
position: OrderObjectPositionTypePB.Before,
|
||||
@@ -51,7 +51,7 @@ class RowBackendService {
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<RowMetaPB, FlowyError>> createRowAfter(RowId rowId) {
|
||||
Future<FlowyResult<RowMetaPB, FlowyError>> createRowAfter(RowId rowId) {
|
||||
return createRow(
|
||||
viewId: viewId,
|
||||
position: OrderObjectPositionTypePB.After,
|
||||
@@ -59,7 +59,7 @@ class RowBackendService {
|
||||
);
|
||||
}
|
||||
|
||||
static Future<Either<RowMetaPB, FlowyError>> getRow({
|
||||
static Future<FlowyResult<RowMetaPB, FlowyError>> getRow({
|
||||
required String viewId,
|
||||
required String rowId,
|
||||
}) {
|
||||
@@ -70,7 +70,7 @@ class RowBackendService {
|
||||
return DatabaseEventGetRowMeta(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<RowMetaPB, FlowyError>> getRowMeta(RowId rowId) {
|
||||
Future<FlowyResult<RowMetaPB, FlowyError>> getRowMeta(RowId rowId) {
|
||||
final payload = RowIdPB.create()
|
||||
..viewId = viewId
|
||||
..rowId = rowId;
|
||||
@@ -78,7 +78,7 @@ class RowBackendService {
|
||||
return DatabaseEventGetRowMeta(payload).send();
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> updateMeta({
|
||||
Future<FlowyResult<void, FlowyError>> updateMeta({
|
||||
required String rowId,
|
||||
String? iconURL,
|
||||
String? coverURL,
|
||||
@@ -102,7 +102,7 @@ class RowBackendService {
|
||||
return DatabaseEventUpdateRowMeta(payload).send();
|
||||
}
|
||||
|
||||
static Future<Either<Unit, FlowyError>> deleteRow(
|
||||
static Future<FlowyResult<void, FlowyError>> deleteRow(
|
||||
String viewId,
|
||||
RowId rowId,
|
||||
) {
|
||||
@@ -113,7 +113,7 @@ class RowBackendService {
|
||||
return DatabaseEventDeleteRow(payload).send();
|
||||
}
|
||||
|
||||
static Future<Either<Unit, FlowyError>> duplicateRow(
|
||||
static Future<FlowyResult<void, FlowyError>> duplicateRow(
|
||||
String viewId,
|
||||
RowId rowId,
|
||||
) {
|
||||
|
||||
@@ -2,14 +2,13 @@ import 'dart:async';
|
||||
|
||||
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_info.dart';
|
||||
import 'package:appflowy/plugins/database/domain/group_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/board_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../group/group_service.dart';
|
||||
|
||||
part 'group_bloc.freezed.dart';
|
||||
|
||||
class DatabaseGroupBloc extends Bloc<DatabaseGroupEvent, DatabaseGroupState> {
|
||||
|
||||
+2
-3
@@ -2,14 +2,13 @@ import 'dart:async';
|
||||
|
||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_info.dart';
|
||||
import 'package:appflowy/plugins/database/application/field_settings/field_settings_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_settings_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_settings_entities.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../field/field_service.dart';
|
||||
|
||||
part 'property_bloc.freezed.dart';
|
||||
|
||||
class DatabasePropertyBloc
|
||||
|
||||
+11
-7
@@ -1,13 +1,14 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:appflowy/core/notification/grid_notification.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/notification.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
|
||||
typedef UpdateSettingNotifiedValue = Either<DatabaseViewSettingPB, FlowyError>;
|
||||
typedef UpdateSettingNotifiedValue
|
||||
= FlowyResult<DatabaseViewSettingPB, FlowyError>;
|
||||
|
||||
class DatabaseSettingListener {
|
||||
DatabaseSettingListener({required this.viewId});
|
||||
@@ -26,14 +27,17 @@ class DatabaseSettingListener {
|
||||
DatabaseNotificationListener(objectId: viewId, handler: _handler);
|
||||
}
|
||||
|
||||
void _handler(DatabaseNotification ty, Either<Uint8List, FlowyError> result) {
|
||||
void _handler(
|
||||
DatabaseNotification ty,
|
||||
FlowyResult<Uint8List, FlowyError> result,
|
||||
) {
|
||||
switch (ty) {
|
||||
case DatabaseNotification.DidUpdateSettings:
|
||||
result.fold(
|
||||
(payload) => _updateSettingNotifier?.value = left(
|
||||
(payload) => _updateSettingNotifier?.value = FlowyResult.success(
|
||||
DatabaseViewSettingPB.fromBuffer(payload),
|
||||
),
|
||||
(error) => _updateSettingNotifier?.value = right(error),
|
||||
(error) => _updateSettingNotifier?.value = FlowyResult.failure(error),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
|
||||
+4
-4
@@ -1,15 +1,15 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
|
||||
class SettingBackendService {
|
||||
const SettingBackendService({required this.viewId});
|
||||
|
||||
final String viewId;
|
||||
|
||||
Future<Either<DatabaseViewSettingPB, FlowyError>> getSetting() {
|
||||
Future<FlowyResult<DatabaseViewSettingPB, FlowyError>> getSetting() {
|
||||
final payload = DatabaseViewIdPB.create()..value = viewId;
|
||||
return DatabaseEventGetDatabaseSetting(payload).send();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/workspace/application/settings/share/export_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-document/entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'share_bloc.freezed.dart';
|
||||
|
||||
class DatabaseShareBloc extends Bloc<DatabaseShareEvent, DatabaseShareState> {
|
||||
@@ -35,9 +37,9 @@ class DatabaseShareBloc extends Bloc<DatabaseShareEvent, DatabaseShareState> {
|
||||
result.fold(
|
||||
(l) {
|
||||
_saveCSVToPath(l.data, event.path);
|
||||
return left(unit);
|
||||
return FlowyResult.success(null);
|
||||
},
|
||||
(r) => right(r),
|
||||
(r) => FlowyResult.failure(r),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -61,6 +63,6 @@ class DatabaseShareState with _$DatabaseShareState {
|
||||
const factory DatabaseShareState.initial() = _Initial;
|
||||
const factory DatabaseShareState.loading() = _Loading;
|
||||
const factory DatabaseShareState.finish(
|
||||
Either<Unit, FlowyError> successOrFail,
|
||||
FlowyResult<void, FlowyError> successOrFail,
|
||||
) = _Finish;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:appflowy/plugins/database/domain/database_view_service.dart';
|
||||
import 'package:appflowy/plugins/database/tab_bar/tab_bar_view.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/database_layout_ext.dart';
|
||||
import 'package:appflowy/workspace/application/view/prelude.dart';
|
||||
@@ -11,7 +12,6 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import 'database_controller.dart';
|
||||
import 'database_view_service.dart';
|
||||
|
||||
part 'tab_bar_bloc.freezed.dart';
|
||||
|
||||
|
||||
+24
-19
@@ -1,19 +1,20 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:appflowy/core/notification/grid_notification.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/sort_entities.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/notification.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/sort_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/view_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
|
||||
typedef RowsVisibilityNotifierValue
|
||||
= Either<RowsVisibilityChangePB, FlowyError>;
|
||||
= FlowyResult<RowsVisibilityChangePB, FlowyError>;
|
||||
|
||||
typedef NumberOfRowsNotifierValue = Either<RowsChangePB, FlowyError>;
|
||||
typedef ReorderAllRowsNotifierValue = Either<List<String>, FlowyError>;
|
||||
typedef SingleRowNotifierValue = Either<ReorderSingleRowPB, FlowyError>;
|
||||
typedef NumberOfRowsNotifierValue = FlowyResult<RowsChangePB, FlowyError>;
|
||||
typedef ReorderAllRowsNotifierValue = FlowyResult<List<String>, FlowyError>;
|
||||
typedef SingleRowNotifierValue = FlowyResult<ReorderSingleRowPB, FlowyError>;
|
||||
|
||||
class DatabaseViewListener {
|
||||
DatabaseViewListener({required this.viewId});
|
||||
@@ -51,34 +52,38 @@ class DatabaseViewListener {
|
||||
_reorderSingleRow?.addPublishListener(onReorderSingleRow);
|
||||
}
|
||||
|
||||
void _handler(DatabaseNotification ty, Either<Uint8List, FlowyError> result) {
|
||||
void _handler(
|
||||
DatabaseNotification ty,
|
||||
FlowyResult<Uint8List, FlowyError> result,
|
||||
) {
|
||||
switch (ty) {
|
||||
case DatabaseNotification.DidUpdateViewRowsVisibility:
|
||||
result.fold(
|
||||
(payload) => _rowsVisibility?.value =
|
||||
left(RowsVisibilityChangePB.fromBuffer(payload)),
|
||||
(error) => _rowsVisibility?.value = right(error),
|
||||
FlowyResult.success(RowsVisibilityChangePB.fromBuffer(payload)),
|
||||
(error) => _rowsVisibility?.value = FlowyResult.failure(error),
|
||||
);
|
||||
break;
|
||||
case DatabaseNotification.DidUpdateViewRows:
|
||||
result.fold(
|
||||
(payload) =>
|
||||
_rowsNotifier?.value = left(RowsChangePB.fromBuffer(payload)),
|
||||
(error) => _rowsNotifier?.value = right(error),
|
||||
(payload) => _rowsNotifier?.value =
|
||||
FlowyResult.success(RowsChangePB.fromBuffer(payload)),
|
||||
(error) => _rowsNotifier?.value = FlowyResult.failure(error),
|
||||
);
|
||||
break;
|
||||
case DatabaseNotification.DidReorderRows:
|
||||
result.fold(
|
||||
(payload) => _reorderAllRows?.value =
|
||||
left(ReorderAllRowsPB.fromBuffer(payload).rowOrders),
|
||||
(error) => _reorderAllRows?.value = right(error),
|
||||
(payload) => _reorderAllRows?.value = FlowyResult.success(
|
||||
ReorderAllRowsPB.fromBuffer(payload).rowOrders,
|
||||
),
|
||||
(error) => _reorderAllRows?.value = FlowyResult.failure(error),
|
||||
);
|
||||
break;
|
||||
case DatabaseNotification.DidReorderSingleRow:
|
||||
result.fold(
|
||||
(payload) => _reorderSingleRow?.value =
|
||||
left(ReorderSingleRowPB.fromBuffer(payload)),
|
||||
(error) => _reorderSingleRow?.value = right(error),
|
||||
FlowyResult.success(ReorderSingleRowPB.fromBuffer(payload)),
|
||||
(error) => _reorderSingleRow?.value = FlowyResult.failure(error),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:appflowy/plugins/database/application/defines.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_info.dart';
|
||||
import 'package:appflowy/plugins/database/application/group/group_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/group_service.dart';
|
||||
import 'package:appflowy/plugins/database/application/row/row_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_board/appflowy_board.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:protobuf/protobuf.dart' hide FieldInfo;
|
||||
@@ -20,7 +19,6 @@ import 'package:protobuf/protobuf.dart' hide FieldInfo;
|
||||
import '../../application/database_controller.dart';
|
||||
import '../../application/field/field_controller.dart';
|
||||
import '../../application/row/row_cache.dart';
|
||||
|
||||
import 'group_controller.dart';
|
||||
|
||||
part 'board_bloc.freezed.dart';
|
||||
@@ -141,10 +139,10 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
_groupItemStartEditing(group, row, true);
|
||||
},
|
||||
didReceiveGridUpdate: (DatabasePB grid) {
|
||||
emit(state.copyWith(grid: Some(grid)));
|
||||
emit(state.copyWith(grid: grid));
|
||||
},
|
||||
didReceiveError: (FlowyError error) {
|
||||
emit(state.copyWith(noneOrError: some(error)));
|
||||
emit(state.copyWith(noneOrError: error));
|
||||
},
|
||||
didReceiveGroups: (List<GroupPB> groups) {
|
||||
final hiddenGroups = _filterHiddenGroups(hideUngrouped, groups);
|
||||
@@ -450,11 +448,15 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
(grid) {
|
||||
databaseController.setIsLoading(false);
|
||||
emit(
|
||||
state.copyWith(loadingState: LoadingState.finish(left(unit))),
|
||||
state.copyWith(
|
||||
loadingState: LoadingState.finish(FlowyResult.success(null)),
|
||||
),
|
||||
);
|
||||
},
|
||||
(err) => emit(
|
||||
state.copyWith(loadingState: LoadingState.finish(right(err))),
|
||||
state.copyWith(
|
||||
loadingState: LoadingState.finish(FlowyResult.failure(err)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -543,12 +545,12 @@ class BoardEvent with _$BoardEvent {
|
||||
class BoardState with _$BoardState {
|
||||
const factory BoardState({
|
||||
required String viewId,
|
||||
required Option<DatabasePB> grid,
|
||||
required DatabasePB? grid,
|
||||
required List<String> groupIds,
|
||||
required bool isEditingHeader,
|
||||
required bool isEditingRow,
|
||||
required LoadingState loadingState,
|
||||
required Option<FlowyError> noneOrError,
|
||||
required FlowyError? noneOrError,
|
||||
required BoardLayoutSettingPB? layoutSettings,
|
||||
String? editingHeaderId,
|
||||
BoardEditingRow? editingRow,
|
||||
@@ -557,12 +559,12 @@ class BoardState with _$BoardState {
|
||||
}) = _BoardState;
|
||||
|
||||
factory BoardState.initial(String viewId) => BoardState(
|
||||
grid: none(),
|
||||
grid: null,
|
||||
viewId: viewId,
|
||||
groupIds: [],
|
||||
isEditingHeader: false,
|
||||
isEditingRow: false,
|
||||
noneOrError: none(),
|
||||
noneOrError: null,
|
||||
loadingState: const LoadingState.loading(),
|
||||
layoutSettings: null,
|
||||
hiddenGroups: [],
|
||||
|
||||
+10
-9
@@ -1,12 +1,12 @@
|
||||
import 'package:appflowy/plugins/database/application/row/row_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:appflowy/core/notification/grid_notification.dart';
|
||||
import 'package:appflowy/plugins/database/application/row/row_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:protobuf/protobuf.dart';
|
||||
|
||||
typedef OnGroupError = void Function(FlowyError);
|
||||
@@ -112,7 +112,8 @@ class GroupController {
|
||||
}
|
||||
}
|
||||
|
||||
typedef UpdateGroupNotifiedValue = Either<GroupRowsNotificationPB, FlowyError>;
|
||||
typedef UpdateGroupNotifiedValue
|
||||
= FlowyResult<GroupRowsNotificationPB, FlowyError>;
|
||||
|
||||
class SingleGroupListener {
|
||||
SingleGroupListener(this.group);
|
||||
@@ -134,14 +135,14 @@ class SingleGroupListener {
|
||||
|
||||
void _handler(
|
||||
DatabaseNotification ty,
|
||||
Either<Uint8List, FlowyError> result,
|
||||
FlowyResult<Uint8List, FlowyError> result,
|
||||
) {
|
||||
switch (ty) {
|
||||
case DatabaseNotification.DidUpdateGroupRow:
|
||||
result.fold(
|
||||
(payload) => _groupNotifier?.value =
|
||||
left(GroupRowsNotificationPB.fromBuffer(payload)),
|
||||
(error) => _groupNotifier?.value = right(error),
|
||||
FlowyResult.success(GroupRowsNotificationPB.fromBuffer(payload)),
|
||||
(error) => _groupNotifier?.value = FlowyResult.failure(error),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
|
||||
Loaded 100 of 677 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user