Merge remote-tracking branch 'origin/main' into develop

# Conflicts:
#	frontend/appflowy_flutter/lib/plugins/database_view/application/database_view_service.dart
#	frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/base/link_to_page_widget.dart
#	frontend/appflowy_flutter/lib/plugins/trash/application/trash_bloc.dart
#	frontend/appflowy_flutter/lib/plugins/trash/application/trash_service.dart
#	frontend/appflowy_flutter/lib/workspace/application/app/app_bloc.dart
#	frontend/appflowy_flutter/lib/workspace/application/app/app_service.dart
#	frontend/appflowy_flutter/lib/workspace/application/menu/menu_bloc.dart
#	frontend/appflowy_flutter/lib/workspace/application/workspace/workspace_service.dart
#	frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/header.dart
#	frontend/appflowy_flutter/test/bloc_test/grid_test/filter/filter_util.dart
#	frontend/appflowy_flutter/test/bloc_test/grid_test/grid_bloc_test.dart
#	frontend/appflowy_flutter/test/bloc_test/grid_test/util.dart
#	frontend/appflowy_flutter/test/bloc_test/home_test/view_bloc_test.dart
#	frontend/rust-lib/flowy-database/src/services/database/database_editor.rs
#	frontend/rust-lib/flowy-database/src/services/persistence/migration/database_view_migration.rs
This commit is contained in:
Lucas.Xu
2023-04-18 19:06:21 +08:00
683 changed files with 4329 additions and 39219 deletions

View File

@ -1,4 +1 @@
frontend/appflowy_flutter/ .git
frontend/scripts/
frontend/rust-lib/target
shared-lib/target/

47
.github/workflows/docker_ci.yml vendored Normal file
View File

@ -0,0 +1,47 @@
name: Docker-CI
on:
push:
branches:
- main
- release/*
paths:
- frontend/**
pull_request:
branches:
- main
- release/*
paths:
- frontend/**
types:
- opened
- synchronize
- reopened
- unlocked
- ready_for_review
jobs:
build-app:
if: github.event.pull_request.draft != true
concurrency:
group: docker_ci-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v3
- name: Build the app
shell: bash
run: |
set -eu -o pipefail
cd frontend/scripts/docker-buildfiles
docker-compose build --no-cache --progress=plain \
| while read line; do \
if [[ "$line" =~ ^Step[[:space:]] ]]; then \
echo "$(date -u '+%H:%M:%S') | $line"; \
else \
echo "$line"; \
fi; \
done \

View File

@ -99,7 +99,8 @@ jobs:
- name: Flutter Analyzer - name: Flutter Analyzer
working-directory: frontend/appflowy_flutter working-directory: frontend/appflowy_flutter
run: flutter analyze run: |
flutter analyze .
- name: Run Flutter unit tests - name: Run Flutter unit tests
working-directory: frontend working-directory: frontend

View File

@ -22,7 +22,7 @@ jobs:
tests: tests:
strategy: strategy:
matrix: matrix:
os: [macos-latest] os: [ubuntu-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@ -105,11 +105,14 @@ jobs:
working-directory: frontend/appflowy_flutter working-directory: frontend/appflowy_flutter
run: | run: |
if [ "$RUNNER_OS" == "Linux" ]; then if [ "$RUNNER_OS" == "Linux" ]; then
flutter test integration_test/runner.dart -d Linux --coverage export DISPLAY=:99
sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 &
sudo apt-get install network-manager
flutter test integration_test/runner.dart -d Linux --coverage --verbose
elif [ "$RUNNER_OS" == "macOS" ]; then elif [ "$RUNNER_OS" == "macOS" ]; then
flutter test integration_test/runner.dart -d macOS --coverage flutter test integration_test/runner.dart -d macOS --coverage --verbose
elif [ "$RUNNER_OS" == "Windows" ]; then elif [ "$RUNNER_OS" == "Windows" ]; then
flutter test integration_test/runner.dart -d Windows --coverage flutter test integration_test/runner.dart -d Windows --coverage --verbose
fi fi
shell: bash shell: bash

View File

@ -205,12 +205,17 @@ script = [
] ]
script_runner = "@duckscript" script_runner = "@duckscript"
[env.test-macos] [env.test-macos-x86_64]
TEST_CRATE_TYPE = "cdylib" TEST_CRATE_TYPE = "cdylib"
TEST_LIB_EXT = "dylib" TEST_LIB_EXT = "dylib"
# For the moment, the DynamicLibrary only supports open x86_64 architectures binary. # For the moment, the DynamicLibrary only supports open x86_64 architectures binary.
TEST_COMPILE_TARGET = "x86_64-apple-darwin" TEST_COMPILE_TARGET = "x86_64-apple-darwin"
[env.test-macos-arm64]
TEST_CRATE_TYPE = "cdylib"
TEST_LIB_EXT = "dylib"
TEST_COMPILE_TARGET = "aarch64-apple-darwin"
[env.test-linux] [env.test-linux]
TEST_CRATE_TYPE = "cdylib" TEST_CRATE_TYPE = "cdylib"
TEST_LIB_EXT = "so" TEST_LIB_EXT = "so"

View File

@ -14,9 +14,7 @@ analyzer:
exclude: exclude:
- "**/*.g.dart" - "**/*.g.dart"
- "**/*.freezed.dart" - "**/*.freezed.dart"
- "packages/appflowy_editor/**"
- "packages/editor/**"
# - "packages/flowy_infra_ui/**"
linter: linter:
# The lint rules applied to this project can be customized in the # The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml` # section below to disable rules from the `package:flutter_lints/flutter.yaml`
@ -30,8 +28,7 @@ linter:
# `// ignore_for_file: name_of_lint` syntax on the line or in the file # `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint. # producing the lint.
rules: rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule - require_trailing_commas
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at # Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options # https://dart.dev/guides/language/analysis-options

View File

@ -1,112 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 761.68 143.1"><title>Asset 16</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path d="M271,40.73h18v6.7c4.37-4.68,12.64-8.42,21.69-8.42C330,39,342.5,54,342.5,74.74c0,21.38-14.67,37.92-37.14,37.92-6.24,0-12.32-1.09-16.38-3.44v32.93H271Zm18,22.78v28.4c4.84,3.12,8.89,4,14.82,4,12.48,0,19.66-9,19.66-20.75,0-11.24-6.55-19.35-18.41-19.35C299,55.86,293.35,58.67,289,63.51Z" style="fill:#fff"/><path d="M226.66,39.4c-22.48,0-37.14,16.54-37.14,37.91,0,20.76,12.47,35.74,31.83,35.74,9,0,17.31-3.75,21.68-8.43v6.72h18V52.42C247.68,42.36,232.89,39.4,226.66,39.4ZM243,88.55c-4.37,4.83-10,7.64-16.07,7.64-11.85,0-18.41-8.11-18.41-19.34,0-11.7,7.18-20.76,19.66-20.76,5.93,0,10,.94,14.82,4.07Z" style="fill:#fff"/><path d="M350.16,40.73h17.95v6.7c4.36-4.68,12.63-8.42,21.68-8.42,19.35,0,31.84,15,31.84,35.73,0,21.38-14.67,37.92-37.14,37.92-6.24,0-12.33-1.09-16.38-3.44v32.93H350.16Zm17.95,22.78v28.4c4.83,3.12,8.89,4,14.82,4,12.48,0,19.66-9,19.66-20.75,0-11.24-6.55-19.35-18.41-19.35C378.09,55.86,372.47,58.67,368.11,63.51Z" style="fill:#fff"/><path d="M473.63,20.6a20.34,20.34,0,0,0-11.08-3.75c-8.11,0-12.32,4.22-12.32,13.89v10h12.64V57.58H450.23v53.36h-18V29.34c0-19.51,9.36-29.34,25-29.34,7.18,0,13.73,1.72,18.41,4.68h15.92V84.26c0,8,2,11.39,6.08,11.39a12.6,12.6,0,0,0,7.49-2.81l3.75,13.73c-4.06,3.28-10.15,5.93-18.57,5.93-10.77,0-16.7-6.55-16.7-20.91Z" style="fill:#fff"/><path d="M547.12,39c23.25,0,38.23,15.76,38.23,36.82s-15,36.83-38.23,36.83S508.9,96.9,508.9,75.83,523.87,39,547.12,39Zm0,57c11.08,0,19.2-8,19.2-20.13,0-11.86-8.27-20-19.2-20-10.61,0-19,7.8-19,20C528.09,87.54,536.2,96,547.12,96Z" style="fill:#fff"/><path d="M674.14,111.25H658.07l-15-39c-1.1-2.65-1.56-5.61-2.34-8.27a50.32,50.32,0,0,1-2.5,8.59l-14.82,38.69h-15.3L585.35,40.73h19.2L615.78,79a73.76,73.76,0,0,1,2,9.37A91.46,91.46,0,0,1,620.46,79l13.11-38.22h15.91L663.06,78.8a98.26,98.26,0,0,1,2.65,9.67c.63-3.28,1.41-6.71,2.18-10.14l10.3-37.6h18.1Z" style="fill:#fff"/><path d="M734.06,110.94c-10.3,25.12-16.85,31.21-27.15,31.21-6.4,0-11.08-2.5-15.14-5.46l5.78-13.58c1.87,1.09,4.52,2.5,7.33,2.5,4.53,0,7.8-3.59,10.77-10.92l1.56-3.75L684.75,40.73h21.54l16.38,38.38c1.4,3.43,2.34,6.71,3.43,10.14A71.08,71.08,0,0,1,728.6,79l12.64-38.22h20.44Z" style="fill:#fff"/><path d="M140.91,87.45c-4,21.87-21.71,41-42.18,51.75-2.57,1.35-3.58,2.12-6.47,2.3h41.49a7,7,0,0,0,7.2-7V87.45Z" style="fill:#f7931e"/><path d="M95.68,140.24a15.15,15.15,0,0,1-2.52,0Z" style="fill:#ffce00"/><path d="M54.36,45.23c-.63.51-1.25,1-1.89,1.44C42,54.06,9.9,78,2.65,67.71-4.46,57.58,3,28.86,21.49,15c.35-.28.71-.53,1.06-.78,20-14.09,35-12.13,42.28-1.81C71.63,22.13,64,37.31,54.36,45.23Z" style="fill:#8427e0"/><path d="M132.72,67.41c-9.91,7-25.55-1.17-33.32-11.14-.31-.4-.61-.81-.91-1.22-7.38-10.51-31.34-42.57-21-49.82s39.94.67,53.45,19.9c.3.43.59.85.87,1.27C144.91,45.8,142.81,60.31,132.72,67.41Z" style="fill:#00b5ff"/><path d="M119.45,130.66c-.34.26-.69.52-1,.76-20,14.09-35,12.12-42.28,1.82-6.79-9.68.8-24.85,10.46-32.78.62-.52,1.26-1,1.9-1.45C99,91.62,131.06,67.67,138.31,78,145.42,88.1,137.93,116.83,119.45,130.66Z" style="fill:#ffbd00"/><path d="M63.51,140.45c-10.32,7.24-39.94-.67-53.45-19.91-.28-.38-.54-.77-.8-1.16a0,0,0,0,0,0,0c-13.19-19.44-11.1-34-1-41.08,9.9-7,25.55,1.15,33.31,11.12.31.4.62.82.91,1.23C49.86,101.14,73.82,133.2,63.51,140.45Z" style="fill:#e3006d"/><path d="M54.36,45.23C40,51.53,9.65,64.64,5.08,54.12,1.25,45.3,8.34,27.25,21.49,15c.35-.28.71-.53,1.06-.78,20-14.09,35-12.13,42.28-1.81C71.63,22.13,64,37.31,54.36,45.23Z" style="fill:#9327ff"/><path d="M132.72,67.41c-9.91,7-25.55-1.17-33.32-11.14C93,41.57,80.68,12.49,91,8c9.26-4,28.65,4,40.83,18.37C144.91,45.8,142.81,60.31,132.72,67.41Z" style="fill:#00c8ff"/><path d="M119.45,130.66c-.34.26-.69.52-1,.76-20,14.09-35,12.12-42.28,1.82-6.79-9.68.8-24.85,10.46-32.78,14.32-6.3,44.72-19.42,49.29-8.91C139.71,100.38,132.62,118.44,119.45,130.66Z" style="fill:#ffce00"/><path d="M50,137.65c-9.23,4-28.57-3.94-40.75-18.27a0,0,0,0,0,0,0c-13.19-19.44-11.1-34-1-41.08,9.9-7,25.55,1.15,33.31,11.12C48,104.11,60.28,133.2,50,137.65Z" style="fill:#fb006d"/></g></g></svg>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
id="svg873"
version="1.1"
fill="none"
viewBox="0 0 92 17"
height="17"
width="92">
<metadata
id="metadata877">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="g866"
clip-path="url(#clip0)">
<path
style="fill:#ffffff"
id="path834"
fill="black"
d="M32.7324 4.84144H34.892V5.63601C35.4179 5.08166 36.4132 4.63818 37.5212 4.63818C39.8498 4.63818 41.3709 6.4121 41.3709 8.88818C41.3709 11.4197 39.6057 13.3969 36.8827 13.3969C36.1315 13.3969 35.3991 13.2675 34.9108 12.9904V16.9078H32.7512V4.84144H32.7324ZM34.9108 7.53927V10.9208C35.493 11.2904 35.9812 11.4012 36.6949 11.4012C38.1972 11.4012 39.0611 10.3295 39.0611 8.94362C39.0611 7.61318 38.2723 6.65231 36.8451 6.65231C36.1127 6.63384 35.4366 6.96644 34.9108 7.53927Z" />
<path
style="fill:#ffffff"
id="path836"
fill="black"
d="M27.3804 4.6748C24.6573 4.6748 22.8921 6.6335 22.8921 9.1835C22.8921 11.6596 24.3944 13.4335 26.7419 13.4335C27.8311 13.4335 28.8264 12.99 29.371 12.4357V13.2302H31.5306V6.22698C29.9156 5.02589 28.1315 4.6748 27.3804 4.6748ZM29.3522 10.5324C28.8264 11.1052 28.1503 11.4378 27.4179 11.4378C25.9907 11.4378 25.2019 10.477 25.2019 9.14654C25.2019 7.76067 26.0658 6.67046 27.5681 6.67046C28.2818 6.67046 28.77 6.78133 29.3522 7.15089V10.5324Z" />
<path
style="fill:#ffffff"
id="path838"
fill="black"
d="M42.291 4.84144H44.4506V5.63601C44.9765 5.08166 45.9718 4.63818 47.0797 4.63818C49.4084 4.63818 50.9295 6.4121 50.9295 8.88818C50.9295 11.4197 49.1642 13.3969 46.4412 13.3969C45.6901 13.3969 44.9577 13.2675 44.4694 12.9904V16.9078H42.3098V4.84144H42.291ZM44.4694 7.53927V10.9208C45.0516 11.2904 45.5398 11.4012 46.2535 11.4012C47.7558 11.4012 48.6196 10.3295 48.6196 8.94362C48.6196 7.61318 47.8309 6.65231 46.4037 6.65231C45.6713 6.63384 44.9952 6.96644 44.4694 7.53927Z" />
<path
style="fill:#ffffff"
id="path840"
fill="black"
d="M57.2019 2.43913C56.8826 2.21739 56.3756 1.99565 55.8685 1.99565C54.892 1.99565 54.3849 2.49457 54.3849 3.64022V4.82283H55.9061V6.83696H54.3849V13.175H52.2065V3.49239C52.2065 1.18261 53.3333 0 55.23 0C56.0939 0 56.8826 0.203261 57.446 0.554348H59.3615V10.0152C59.3615 10.9576 59.6056 11.3641 60.0939 11.3641C60.4507 11.3641 60.7324 11.2163 60.9953 11.0315L61.446 12.6576C60.9577 13.0457 60.2253 13.3598 59.2112 13.3598C57.9155 13.3598 57.2019 12.5837 57.2019 10.8837V2.43913Z" />
<path
style="fill:#ffffff"
id="path842"
fill="black"
d="M66.0846 4.63818C68.9015 4.63818 70.7043 6.50449 70.7043 9.01753C70.7043 11.5121 68.9015 13.3969 66.0846 13.3969C63.2677 13.3969 61.4648 11.5306 61.4648 9.01753C61.4648 6.50449 63.2864 4.63818 66.0846 4.63818ZM66.0846 11.4012C67.4179 11.4012 68.3944 10.4588 68.3944 9.01753C68.3944 7.61318 67.3991 6.65231 66.0846 6.65231C64.8076 6.65231 63.7935 7.57623 63.7935 9.01753C63.7935 10.4034 64.77 11.4012 66.0846 11.4012Z" />
<path
style="fill:#ffffff"
id="path844"
fill="black"
d="M81.4271 13.2307H79.4928L77.69 8.59264C77.5586 8.27851 77.5022 7.92742 77.4083 7.61329C77.3332 7.98286 77.2393 8.29699 77.1079 8.6296L75.305 13.2307H73.4647L70.7041 4.84155H73.014L74.3661 9.38721C74.4975 9.79373 74.5351 10.1079 74.6102 10.4959C74.7041 10.1448 74.7604 9.84916 74.9295 9.38721L76.5069 4.84155H78.4224L80.0562 9.36873C80.1877 9.73829 80.2816 10.1263 80.3755 10.5144C80.4506 10.1263 80.5445 9.71981 80.6384 9.31329L81.8778 4.84155H84.0562L81.4271 13.2307Z" />
<path
style="fill:#ffffff"
id="path846"
fill="black"
d="M88.6574 13.1936C87.4179 16.187 86.6292 16.9077 85.371 16.9077C84.601 16.9077 84.0377 16.612 83.5494 16.2609L84.2442 14.6533C84.4696 14.7827 84.7888 14.949 85.1269 14.949C85.6715 14.949 86.0658 14.524 86.4226 13.6555L86.6104 13.212L82.6855 4.85986H85.2959L87.2677 9.42399C87.4367 9.83052 87.5494 10.2186 87.6808 10.6251C87.756 10.237 87.8311 9.83052 87.9813 9.40552L89.5024 4.85986H91.9625L88.6574 13.1936Z" />
<path
id="path848"
fill="#F7931E"
d="M17.0142 10.4033C16.5259 13.0088 14.3851 15.2816 11.925 16.5566C11.6245 16.7229 11.2677 16.8153 10.9297 16.8338H16.1503C16.6574 16.8338 17.0142 16.4642 17.0142 16.0022V10.4033Z" />
<path
id="path850"
fill="#8427E0"
d="M6.57258 5.37743C6.49746 5.43286 6.42235 5.4883 6.34723 5.54373C5.07023 6.43069 1.20169 9.27634 0.319059 8.0383C-0.544791 6.83721 0.375397 3.43721 2.59136 1.79264C2.62892 1.75569 2.68526 1.73721 2.72281 1.70025C5.14535 0.0187313 6.94817 0.258949 7.8308 1.47851C8.65709 2.62417 7.7369 4.43504 6.57258 5.37743Z" />
<path
id="path852"
fill="#00B5FF"
d="M16.0373 8.02043C14.8354 8.85195 12.9575 7.89108 12.0185 6.69C11.981 6.63456 11.9434 6.59761 11.9059 6.54217C11.0045 5.28565 8.11244 1.47913 9.35188 0.629126C10.5913 -0.239352 14.1782 0.703039 15.812 2.99434C15.8495 3.04978 15.8871 3.08674 15.9246 3.14217C17.5021 5.45195 17.258 7.17043 16.0373 8.02043Z" />
<path
id="path854"
fill="#FFBD00"
d="M14.4226 15.5219C14.385 15.5589 14.3475 15.5773 14.2911 15.6143C11.8686 17.2958 10.0658 17.0556 9.18315 15.836C8.35686 14.6904 9.27705 12.8795 10.4414 11.9371C10.5165 11.8817 10.5916 11.8263 10.6667 11.7708C11.9437 10.9023 15.8123 8.03821 16.6761 9.27625C17.5587 10.4773 16.6573 13.8773 14.4226 15.5219Z" />
<path
id="path856"
fill="#E3006D"
d="M7.66182 16.6859C6.42238 17.5544 2.85431 16.612 1.22051 14.3207C1.18295 14.2838 1.14539 14.2283 1.12661 14.1914C-0.469635 11.8816 -0.206725 10.1446 1.01393 9.31311C2.21581 8.48159 4.09374 9.44246 5.03271 10.6435C5.07027 10.699 5.10783 10.7359 5.14539 10.7914C6.02802 12.0294 8.92004 15.8359 7.66182 16.6859Z" />
<path
id="path858"
fill="#9327FF"
d="M6.57283 5.37743C4.84513 6.13504 1.16438 7.68721 0.619779 6.43069C0.150295 5.37743 1.01415 3.23395 2.59161 1.79264C2.62917 1.75569 2.68551 1.73721 2.72307 1.70025C5.1456 0.0187312 6.94842 0.258949 7.83105 1.47851C8.65734 2.62417 7.73715 4.43504 6.57283 5.37743Z" />
<path
id="path860"
fill="#00C8FF"
d="M16.0374 8.01967C14.8355 8.8512 12.9576 7.89033 12.0186 6.68924C11.2487 4.9338 9.76511 1.47837 11.0045 0.960978C12.1313 0.480543 14.4599 1.44141 15.9435 3.14141C17.5022 5.4512 17.2581 7.16967 16.0374 8.01967Z" />
<path
id="path862"
fill="#FFCE00"
d="M14.4226 15.5217C14.385 15.5586 14.3475 15.5771 14.2911 15.6141C11.8686 17.2956 10.0658 17.0554 9.18315 15.8358C8.35686 14.6901 9.27705 12.8793 10.4414 11.9369C12.1691 11.1793 15.8498 9.6271 16.3944 10.8836C16.8827 11.9369 16.0188 14.0804 14.4226 15.5217Z" />
<path
id="path864"
fill="#FB006D"
d="M6.0468 16.3533C4.92004 16.8338 2.5914 15.8914 1.12661 14.1914C-0.469635 11.8816 -0.206725 10.1446 1.01393 9.31311C2.21581 8.48159 4.09374 9.44246 5.03271 10.6435C5.80266 12.3805 7.28623 15.8359 6.0468 16.3533Z" />
</g>
<defs
id="defs871">
<clipPath
id="clip0">
<rect
id="rect868"
fill="white"
height="17"
width="92" />
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -1,150 +1,159 @@
{ {
"appName": "AppFlowy", "appName": "AppFlowy",
"defaultUsername": "Ich", "defaultUsername": "Ich",
"welcomeText": "Willkommen bei @:appName", "welcomeText": "Willkommen bei @:appName",
"githubStarText": "GitHub Star vergeben", "githubStarText": "GitHub Star vergeben",
"subscribeNewsletterText": "Abonniere den Newsletter", "subscribeNewsletterText": "Abonniere den Newsletter",
"letsGoButtonText": "Los geht's", "letsGoButtonText": "Los geht's",
"title": "Titel", "title": "Titel",
"signUp": { "signUp": {
"buttonText": "Registrieren", "buttonText": "Registrieren",
"title": "Registriere dich bei @:appName", "title": "Registriere dich bei @:appName",
"getStartedText": "Erste Schritte", "getStartedText": "Erste Schritte",
"emptyPasswordError": "Passwort darf nicht leer sein", "emptyPasswordError": "Passwort darf nicht leer sein",
"repeatPasswordEmptyError": "Passwortwiederholung darf nicht leer sein", "repeatPasswordEmptyError": "Passwortwiederholung darf nicht leer sein",
"unmatchedPasswordError": "Passwörter stimmen nicht überein", "unmatchedPasswordError": "Passwörter stimmen nicht überein",
"alreadyHaveAnAccount": "Bereits registriert?", "alreadyHaveAnAccount": "Bereits registriert?",
"emailHint": "E-Mail", "emailHint": "E-Mail",
"passwordHint": "Passwort", "passwordHint": "Passwort",
"repeatPasswordHint": "Wiederhole Passwort" "repeatPasswordHint": "Wiederhole Passwort"
}, },
"signIn": { "signIn": {
"loginTitle": "Bei @:appName einloggen", "loginTitle": "Bei @:appName einloggen",
"loginButtonText": "Anmelden", "loginButtonText": "Anmelden",
"buttonText": "Anmelden", "buttonText": "Anmelden",
"forgotPassword": "Passwort vergessen?", "forgotPassword": "Passwort vergessen?",
"emailHint": "E-Mail", "emailHint": "E-Mail",
"passwordHint": "Passwort", "passwordHint": "Passwort",
"dontHaveAnAccount": "Du besitzt noch kein Konto?", "dontHaveAnAccount": "Du besitzt noch kein Konto?",
"repeatPasswordEmptyError": "Passwortwiederholung darf nicht leer sein", "repeatPasswordEmptyError": "Passwortwiederholung darf nicht leer sein",
"unmatchedPasswordError": "Passwörter stimmen nicht überein" "unmatchedPasswordError": "Passwörter stimmen nicht überein"
}, },
"workspace": { "workspace": {
"create": "Arbeitsbereich erstellen", "create": "Arbeitsbereich erstellen",
"hint": "Arbeitsbereich", "hint": "Arbeitsbereich",
"notFoundError": "Arbeitsbereich nicht gefunden" "notFoundError": "Arbeitsbereich nicht gefunden"
}, },
"shareAction": { "shareAction": {
"buttonText": "Teilen", "buttonText": "Teilen",
"workInProgress": "Demnächst verfügbar", "workInProgress": "Demnächst verfügbar",
"markdown": "Markdown", "markdown": "Markdown",
"copyLink": "Link kopieren" "copyLink": "Link kopieren"
}, },
"disclosureAction": { "disclosureAction": {
"rename": "Umbenennen", "rename": "Umbenennen",
"delete": "Löschen", "delete": "Löschen",
"duplicate": "Duplizieren" "duplicate": "Duplizieren"
}, },
"blankPageTitle": "Leere Seite", "blankPageTitle": "Leere Seite",
"newPageText": "Neue Seite", "newPageText": "Neue Seite",
"trash": { "trash": {
"text": "Papierkorb", "text": "Papierkorb",
"restoreAll": "Alles wiederherstellen", "restoreAll": "Alles wiederherstellen",
"deleteAll": "Alles löschen", "deleteAll": "Alles löschen",
"pageHeader": { "pageHeader": {
"fileName": "Dateiname", "fileName": "Dateiname",
"lastModified": "Letzte Änderung", "lastModified": "Letzte Änderung",
"created": "Erstellt" "created": "Erstellt"
}
},
"deletePagePrompt": {
"text": "Diese Seite ist im Papierkorb",
"restore": "Seite wiederherstellen",
"deletePermanent": "Dauerhaft löschen"
},
"dialogCreatePageNameHint": "Seitenname",
"questionBubble": {
"whatsNew": "Was gibt es Neues?",
"help": "Hilfe & Support",
"debug": {
"name": "Debug-Informationen",
"success": "Debug-Informationen in die Zwischenablage kopiert!",
"fail": "Debug-Informationen können nicht in die Zwischenablage kopiert werden"
}
},
"menuAppHeader": {
"addPageTooltip": "Schnell eine Seite innerhalb hinzufügen",
"defaultNewPageName": "Unbenannt",
"renameDialog": "Umbenennen"
},
"toolbar": {
"undo": "Rückgängig",
"redo": "Wiederherstellen",
"bold": "Fett",
"italic": "Kursiv",
"underline": "Unterstreichen",
"strike": "Durchstreichen",
"numList": "Nummerierte Liste",
"bulletList": "Aufzählung",
"checkList": "Checkliste",
"inlineCode": "Inline-Code",
"quote": "Zitat",
"header": "Überschrift",
"highlight": "Hervorhebung"
},
"tooltip": {
"lightMode": "In den hellen Modus wechseln",
"darkMode": "In den dunklen Modus wechseln"
},
"contactsPage": {
"title": "Kontakte",
"whatsHappening": "Was geschieht diese Woche?",
"addContact": "Kontakt hinzufügen",
"editContact": "Kontakt bearbeiten"
},
"button": {
"OK": "OK",
"Cancel": "Abbrechen",
"signIn": "Anmelden",
"signOut": "Abmelden",
"complete": "Fertig",
"save": "Speichern"
},
"label": {
"welcome": "Willkommen!",
"firstName": "Vorname",
"middleName": "Zweiter Vorname",
"lastName": "Nachname",
"stepX": "Schritt {X}"
},
"oAuth": {
"err": {
"failedTitle": "Keine Verbindung zu Ihrem Konto möglich.",
"failedMsg": "Bitte vergewissern Sie sich, dass Sie den Anmeldevorgang in Ihrem Browser abgeschlossen haben."
},
"google": {
"title": "GOOGLE ANMELDUNG",
"instruction1": "Um Ihre Google-Kontakte zu importieren, müssen Sie diese Anwendung über Ihren Webbrowser autorisieren.",
"instruction2": "Kopieren Sie diesen Code in Ihre Zwischenablage, indem Sie auf das Symbol klicken oder den Text auswählen:",
"instruction3": "Rufen Sie den folgenden Link in Ihrem Webbrowser auf, und geben Sie den obigen Code ein:",
"instruction4": "Klicken Sie unten auf die Schaltfläche, wenn Sie die Anmeldung abgeschlossen haben:"
}
},
"settings": {
"title": "Einstellungen",
"menu": {
"appearance": "Aussehen",
"language": "Sprache",
"open": "Einstellungen öffnen"
},
"appearance": {
"lightLabel": "Heller Modus",
"darkLabel": "Dunkler Modus"
}
},
"sideBar": {
"openSidebar": "Open sidebar",
"closeSidebar": "Close sidebar"
} }
},
"deletePagePrompt": {
"text": "Diese Seite ist im Papierkorb",
"restore": "Seite wiederherstellen",
"deletePermanent": "Dauerhaft löschen"
},
"dialogCreatePageNameHint": "Seitenname",
"questionBubble": {
"whatsNew": "Was gibt es Neues?",
"help": "Hilfe & Support",
"debug": {
"name": "Debug-Informationen",
"success": "Debug-Informationen in die Zwischenablage kopiert!",
"fail": "Debug-Informationen können nicht in die Zwischenablage kopiert werden"
},
"shortcuts": "Abkürzungen"
},
"menuAppHeader": {
"addPageTooltip": "Schnell eine Seite innerhalb hinzufügen",
"defaultNewPageName": "Unbenannt",
"renameDialog": "Umbenennen"
},
"toolbar": {
"undo": "Rückgängig",
"redo": "Wiederherstellen",
"bold": "Fett",
"italic": "Kursiv",
"underline": "Unterstreichen",
"strike": "Durchstreichen",
"numList": "Nummerierte Liste",
"bulletList": "Aufzählung",
"checkList": "Checkliste",
"inlineCode": "Inline-Code",
"quote": "Zitat",
"header": "Überschrift",
"highlight": "Hervorhebung",
"color": "Farbe"
},
"tooltip": {
"lightMode": "In den hellen Modus wechseln",
"darkMode": "In den dunklen Modus wechseln",
"openAsPage": "Als Seite öffnen"
},
"contactsPage": {
"title": "Kontakte",
"whatsHappening": "Was geschieht diese Woche?",
"addContact": "Kontakt hinzufügen",
"editContact": "Kontakt bearbeiten"
},
"button": {
"OK": "OK",
"Cancel": "Abbrechen",
"signIn": "Anmelden",
"signOut": "Abmelden",
"complete": "Fertig",
"save": "Speichern"
},
"label": {
"welcome": "Willkommen!",
"firstName": "Vorname",
"middleName": "Zweiter Vorname",
"lastName": "Nachname",
"stepX": "Schritt {X}"
},
"oAuth": {
"err": {
"failedTitle": "Keine Verbindung zu Ihrem Konto möglich.",
"failedMsg": "Bitte vergewissern Sie sich, dass Sie den Anmeldevorgang in Ihrem Browser abgeschlossen haben."
},
"google": {
"title": "GOOGLE ANMELDUNG",
"instruction1": "Um Ihre Google-Kontakte zu importieren, müssen Sie diese Anwendung über Ihren Webbrowser autorisieren.",
"instruction2": "Kopieren Sie diesen Code in Ihre Zwischenablage, indem Sie auf das Symbol klicken oder den Text auswählen:",
"instruction3": "Rufen Sie den folgenden Link in Ihrem Webbrowser auf, und geben Sie den obigen Code ein:",
"instruction4": "Klicken Sie unten auf die Schaltfläche, wenn Sie die Anmeldung abgeschlossen haben:"
}
},
"settings": {
"title": "Einstellungen",
"menu": {
"appearance": "Aussehen",
"language": "Sprache",
"open": "Einstellungen öffnen"
},
"appearance": {
"lightLabel": "Heller Modus",
"darkLabel": "Dunkler Modus"
}
},
"sideBar": {
"openSidebar": "Open sidebar",
"closeSidebar": "Close sidebar"
},
"moreAction": {
"small": "klein",
"medium": "mittel",
"large": "groß",
"fontSize": "Schriftgröße",
"import": "Importieren"
} }
}

View File

@ -282,6 +282,7 @@
"dateFormatISO": "Year-Month-Day", "dateFormatISO": "Year-Month-Day",
"dateFormatLocal": "Month/Day/Year", "dateFormatLocal": "Month/Day/Year",
"dateFormatUS": "Year/Month/Day", "dateFormatUS": "Year/Month/Day",
"dateFormatDayMonthYear": "Day/Month/Year",
"timeFormat": "Time format", "timeFormat": "Time format",
"invalidTimeFormat": "Invalid format", "invalidTimeFormat": "Invalid format",
"timeFormatTwelveHour": "12 hour", "timeFormatTwelveHour": "12 hour",
@ -290,7 +291,7 @@
"optionTitle": "Options", "optionTitle": "Options",
"addOption": "Add option", "addOption": "Add option",
"editProperty": "Edit property", "editProperty": "Edit property",
"newColumn": "New column", "newProperty": "New property",
"deleteFieldPromptMessage": "Are you sure? This property will be deleted" "deleteFieldPromptMessage": "Are you sure? This property will be deleted"
}, },
"sort": { "sort": {
@ -383,7 +384,9 @@
"pickFromFiles": "Pick from files", "pickFromFiles": "Pick from files",
"couldNotFetchImage": "Could not fetch image", "couldNotFetchImage": "Could not fetch image",
"imageSavingFailed": "Image Saving Failed", "imageSavingFailed": "Image Saving Failed",
"addIcon": "Add Icon" "addIcon": "Add Icon",
"coverRemoveAlert": "It will be removed from cover after it is deleted.",
"alertDialogConfirmation": "Are you sure, you want to continue?"
} }
} }
}, },
@ -410,4 +413,4 @@
"layoutDateField": "Layout calendar by" "layoutDateField": "Layout calendar by"
} }
} }
} }

View File

@ -271,7 +271,7 @@
"optionTitle": "Aukerak", "optionTitle": "Aukerak",
"addOption": "Gehitu aukera", "addOption": "Gehitu aukera",
"editProperty": "Editatu propietatea", "editProperty": "Editatu propietatea",
"newColumn": "Zutabe berria", "newProperty": "Zutabe berria",
"deleteFieldPromptMessage": "Ziur al zaude? Propietate hau ezabatu egingo da" "deleteFieldPromptMessage": "Ziur al zaude? Propietate hau ezabatu egingo da"
}, },
"sort": { "sort": {
@ -330,4 +330,4 @@
"nextMonth": "Hurrengo hilabetea" "nextMonth": "Hurrengo hilabetea"
} }
} }
} }

View File

@ -195,7 +195,7 @@
"optionTitle": "Options", "optionTitle": "Options",
"addOption": "Ajouter une option", "addOption": "Ajouter une option",
"editProperty": "Modifier la propriété", "editProperty": "Modifier la propriété",
"newColumn": "Nouvelle colonne", "newProperty": "Nouvelle colonne",
"deleteFieldPromptMessage": "Vous voulez supprimer cette propriété ?" "deleteFieldPromptMessage": "Vous voulez supprimer cette propriété ?"
}, },
"row": { "row": {

View File

@ -191,7 +191,7 @@
"optionTitle": "옵션", "optionTitle": "옵션",
"addOption": "옵션 추가", "addOption": "옵션 추가",
"editProperty": "속성 편집", "editProperty": "속성 편집",
"newColumn": "열 추가", "newProperty": "열 추가",
"deleteFieldPromptMessage": "해당 속성을 삭제 하시겠습니까?" "deleteFieldPromptMessage": "해당 속성을 삭제 하시겠습니까?"
}, },
"row": { "row": {

View File

@ -39,7 +39,7 @@
"workInProgress": "Em breve", "workInProgress": "Em breve",
"markdown": "Marcador", "markdown": "Marcador",
"copyLink": "Copiar link" "copyLink": "Copiar link"
}, },
"moreAction": { "moreAction": {
"small": "pequeno", "small": "pequeno",
"medium": "médio", "medium": "médio",
@ -177,7 +177,7 @@
"system": "Adaptar-se ao sistema" "system": "Adaptar-se ao sistema"
}, },
"theme": "Tema" "theme": "Tema"
}, },
"files": { "files": {
"defaultLocation": "Onde os seus dados ficam armazenados", "defaultLocation": "Onde os seus dados ficam armazenados",
"doubleTapToCopy": "Clique duas vezes para copiar o caminho", "doubleTapToCopy": "Clique duas vezes para copiar o caminho",
@ -287,7 +287,7 @@
"optionTitle": "Opções", "optionTitle": "Opções",
"addOption": "Adicioar opção", "addOption": "Adicioar opção",
"editProperty": "Editar propriedade", "editProperty": "Editar propriedade",
"newColumn": "Nova coluna", "newProperty": "Nova coluna",
"deleteFieldPromptMessage": "Tem certeza? Esta propriedade será excluída" "deleteFieldPromptMessage": "Tem certeza? Esta propriedade será excluída"
}, },
"sort": { "sort": {
@ -320,7 +320,7 @@
"pannelTitle": "Escolha uma opção ou crie uma", "pannelTitle": "Escolha uma opção ou crie uma",
"searchOption": "Procurar uma opção" "searchOption": "Procurar uma opção"
}, },
"checklist": { "checklist": {
"panelTitle": "Adicionar um item" "panelTitle": "Adicionar um item"
}, },
"menuName": "Grade" "menuName": "Grade"

View File

@ -1,150 +1,149 @@
{ {
"appName": "AppFlowy", "appName": "AppFlowy",
"defaultUsername": "Me", "defaultUsername": "Me",
"welcomeText": "Bem vindo ao @:appName", "welcomeText": "Bem vindo ao @:appName",
"githubStarText": "Star on GitHub", "githubStarText": "Star on GitHub",
"subscribeNewsletterText": "Inscreve-te ao Newsletter", "subscribeNewsletterText": "Inscreve-te ao Newsletter",
"letsGoButtonText": "Bora", "letsGoButtonText": "Bora",
"title": "Título", "title": "Título",
"signUp": { "signUp": {
"buttonText": "Inscreve-te", "buttonText": "Inscreve-te",
"title": "Inscreve-te ao @:appName", "title": "Inscreve-te ao @:appName",
"getStartedText": "Começar", "getStartedText": "Começar",
"emptyPasswordError": "A palavra-passe não pode estar em branco.", "emptyPasswordError": "A palavra-passe não pode estar em branco.",
"repeatPasswordEmptyError": "Confirmar a palavra-passe não pode estar em branco.", "repeatPasswordEmptyError": "Confirmar a palavra-passe não pode estar em branco.",
"unmatchedPasswordError": "As palavras-passes não coincidem.", "unmatchedPasswordError": "As palavras-passes não coincidem.",
"alreadyHaveAnAccount": "Já possuis uma conta?", "alreadyHaveAnAccount": "Já possuis uma conta?",
"emailHint": "Email", "emailHint": "Email",
"passwordHint": "Password", "passwordHint": "Password",
"repeatPasswordHint": "Confirma a tua password" "repeatPasswordHint": "Confirma a tua password"
}, },
"signIn": { "signIn": {
"loginTitle": "Entre no @:appName", "loginTitle": "Entre no @:appName",
"loginButtonText": "Login", "loginButtonText": "Login",
"buttonText": "Entre", "buttonText": "Entre",
"forgotPassword": "Esqueceste-te da tua palavra-passe?", "forgotPassword": "Esqueceste-te da tua palavra-passe?",
"emailHint": "Email", "emailHint": "Email",
"passwordHint": "Palavra-passe", "passwordHint": "Palavra-passe",
"dontHaveAnAccount": "Não possuis uma conta?", "dontHaveAnAccount": "Não possuis uma conta?",
"repeatPasswordEmptyError": "Confirmar a palavra-passe não pode estar em branco.", "repeatPasswordEmptyError": "Confirmar a palavra-passe não pode estar em branco.",
"unmatchedPasswordError": "As palavras-passes não conferem." "unmatchedPasswordError": "As palavras-passes não conferem."
}, },
"workspace": { "workspace": {
"create": "Cria um ambiente de trabalho", "create": "Cria um ambiente de trabalho",
"hint": "ambiente de trabalho", "hint": "ambiente de trabalho",
"notFoundError": "Ambiente de trabalho não encontrada" "notFoundError": "Ambiente de trabalho não encontrada"
}, },
"shareAction": { "shareAction": {
"buttonText": "Partilhar", "buttonText": "Partilhar",
"workInProgress": "Em breve", "workInProgress": "Em breve",
"markdown": "Markdown", "markdown": "Markdown",
"copyLink": "Copiar o link" "copyLink": "Copiar o link"
}, },
"disclosureAction": { "disclosureAction": {
"rename": "Renomear", "rename": "Renomear",
"delete": "Apagar", "delete": "Apagar",
"duplicate": "Duplicar" "duplicate": "Duplicar"
}, },
"blankPageTitle": "Página em branco", "blankPageTitle": "Página em branco",
"newPageText": "Nova página", "newPageText": "Nova página",
"trash": { "trash": {
"text": "Lixo", "text": "Lixo",
"restoreAll": "Restaurar todos", "restoreAll": "Restaurar todos",
"deleteAll": "Apagar todos", "deleteAll": "Apagar todos",
"pageHeader": { "pageHeader": {
"fileName": "Nome do ficheiro", "fileName": "Nome do ficheiro",
"lastModified": "Última modificação", "lastModified": "Última modificação",
"created": "Criado" "created": "Criado"
}
},
"deletePagePrompt": {
"text": "Esta página está no lixo",
"restore": "Restaurar a página",
"deletePermanent": "Apagar permanentemente"
},
"dialogCreatePageNameHint": "Nome da página",
"questionBubble": {
"whatsNew": "O que há de novo?",
"help": "Ajuda & Suporte",
"debug": {
"name": "Informação de depuração",
"success": "Copiar informação de depuração para o clipboard!",
"fail": "Falha em copiar a informação de depuração para o clipboard"
}
},
"menuAppHeader": {
"addPageTooltip": "Adiciona uma nova página.",
"defaultNewPageName": "Sem título",
"renameDialog": "Renomear"
},
"toolbar": {
"undo": "Desfazer",
"redo": "Refazer",
"bold": "Negrito",
"italic": "Itálico",
"underline": "Sublinhado",
"strike": "Riscado",
"numList": "Lista numerada",
"bulletList": "Lista com marcadores",
"checkList": "Lista de verificação",
"inlineCode": "Embutir código",
"quote": "Citação em bloco",
"header": "Cabeçalho",
"highlight": "Realçar"
},
"tooltip": {
"lightMode": "Mudar para o modo Claro.",
"darkMode": "Mudar para o modo Escuro."
},
"contactsPage": {
"title": "Conctatos",
"whatsHappening": "O que está a acontecer nesta semana?",
"addContact": "Adicionar um conctato",
"editContact": "Editar um conctato"
},
"button": {
"OK": "OK",
"Cancel": "Cancelar",
"signIn": "Entrar",
"signOut": "Sair",
"complete": "Completar",
"save": "Guardar"
},
"label": {
"welcome": "Bem vindo!",
"firstName": "Nome",
"middleName": "Nome do Meio",
"lastName": "Apelido",
"stepX": "Passo {X}"
},
"oAuth": {
"err": {
"failedTitle": "Erro ao conectar à sua conta.",
"failedMsg": "Verifica se concluiste o processo de login no teu navegador."
},
"google": {
"title": "GOOGLE SIGN-IN",
"instruction1": "Para importar os teus Conctatos do Google, tens de autorizar esta aplicação usando o teu navegador web.",
"instruction2": "Copia este código para a tua área de transferências clicando no ícone ou selecionando o texto:",
"instruction3": "Navega até o link a seguir no seu navegador e digite o código acima:",
"instruction4": "Clica no botão abaixo ao concluir a inscrição:"
}
},
"settings": {
"title": "Definições",
"menu": {
"appearance": "Aparência",
"language": "Idioma",
"open": "Abrir as Definições"
},
"appearance": {
"lightLabel": "Modo Claro",
"darkLabel": "Modo Escuro"
}
},
"sideBar": {
"openSidebar": "Open sidebar",
"closeSidebar": "Close sidebar"
} }
},
"deletePagePrompt": {
"text": "Esta página está no lixo",
"restore": "Restaurar a página",
"deletePermanent": "Apagar permanentemente"
},
"dialogCreatePageNameHint": "Nome da página",
"questionBubble": {
"whatsNew": "O que há de novo?",
"help": "Ajuda & Suporte",
"debug": {
"name": "Informação de depuração",
"success": "Copiar informação de depuração para o clipboard!",
"fail": "Falha em copiar a informação de depuração para o clipboard"
}
},
"menuAppHeader": {
"addPageTooltip": "Adiciona uma nova página.",
"defaultNewPageName": "Sem título",
"renameDialog": "Renomear"
},
"toolbar": {
"undo": "Desfazer",
"redo": "Refazer",
"bold": "Negrito",
"italic": "Itálico",
"underline": "Sublinhado",
"strike": "Riscado",
"numList": "Lista numerada",
"bulletList": "Lista com marcadores",
"checkList": "Lista de verificação",
"inlineCode": "Embutir código",
"quote": "Citação em bloco",
"header": "Cabeçalho",
"highlight": "Realçar"
},
"tooltip": {
"lightMode": "Mudar para o modo Claro.",
"darkMode": "Mudar para o modo Escuro."
},
"contactsPage": {
"title": "Conctatos",
"whatsHappening": "O que está a acontecer nesta semana?",
"addContact": "Adicionar um conctato",
"editContact": "Editar um conctato"
},
"button": {
"OK": "OK",
"Cancel": "Cancelar",
"signIn": "Entrar",
"signOut": "Sair",
"complete": "Completar",
"save": "Guardar"
},
"label": {
"welcome": "Bem vindo!",
"firstName": "Nome",
"middleName": "Nome do Meio",
"lastName": "Apelido",
"stepX": "Passo {X}"
},
"oAuth": {
"err": {
"failedTitle": "Erro ao conectar à sua conta.",
"failedMsg": "Verifica se concluiste o processo de login no teu navegador."
},
"google": {
"title": "GOOGLE SIGN-IN",
"instruction1": "Para importar os teus Conctatos do Google, tens de autorizar esta aplicação usando o teu navegador web.",
"instruction2": "Copia este código para a tua área de transferências clicando no ícone ou selecionando o texto:",
"instruction3": "Navega até o link a seguir no seu navegador e digite o código acima:",
"instruction4": "Clica no botão abaixo ao concluir a inscrição:"
}
},
"settings": {
"title": "Definições",
"menu": {
"appearance": "Aparência",
"language": "Idioma",
"open": "Abrir as Definições"
},
"appearance": {
"lightLabel": "Modo Claro",
"darkLabel": "Modo Escuro"
}
},
"sideBar": {
"openSidebar": "Open sidebar",
"closeSidebar": "Close sidebar"
} }
}

View File

@ -1,348 +1,413 @@
{ {
"appName": "AppFlowy", "appName": "AppFlowy",
"defaultUsername": "Я", "defaultUsername": "Я",
"welcomeText": "Добро пожаловать в @:appName", "welcomeText": "Добро пожаловать в @:appName",
"githubStarText": "Поставить звезду на GitHub", "githubStarText": "Поставить звезду на GitHub",
"subscribeNewsletterText": "Подписаться на рассылку", "subscribeNewsletterText": "Подписаться на рассылку",
"letsGoButtonText": "Начнём", "letsGoButtonText": "Начнём",
"title": "Заголовок", "title": "Заголовок",
"signUp": { "signUp": {
"buttonText": "Зарегистрироваться", "buttonText": "Зарегистрироваться",
"title": "Регистрация в @:appName", "title": "Регистрация в @:appName",
"getStartedText": "Начать", "getStartedText": "Начать",
"emptyPasswordError": "Пароль не может быть пустым", "emptyPasswordError": "Пароль не может быть пустым",
"repeatPasswordEmptyError": "Повтор пароля не может быть пустым", "repeatPasswordEmptyError": "Повтор пароля не может быть пустым",
"unmatchedPasswordError": "Пароли не совпадают", "unmatchedPasswordError": "Пароли не совпадают",
"alreadyHaveAnAccount": "Уже есть аккаунт?", "alreadyHaveAnAccount": "Уже есть аккаунт?",
"emailHint": "Электронная почта", "emailHint": "Электронная почта",
"passwordHint": "Пароль", "passwordHint": "Пароль",
"repeatPasswordHint": "Повторите пароль" "repeatPasswordHint": "Повторите пароль"
}, },
"signIn": { "signIn": {
"loginTitle": "Войти в @:appName", "loginTitle": "Войти в @:appName",
"loginButtonText": "Войти", "loginButtonText": "Войти",
"buttonText": "Авторизация", "buttonText": "Авторизация",
"forgotPassword": "Забыли пароль?", "forgotPassword": "Забыли пароль?",
"emailHint": "Электронная почта", "emailHint": "Электронная почта",
"passwordHint": "Пароль", "passwordHint": "Пароль",
"dontHaveAnAccount": "Нет аккаунта?", "dontHaveAnAccount": "Нет аккаунта?",
"repeatPasswordEmptyError": "Повтор пароля не может быть пустым", "repeatPasswordEmptyError": "Повтор пароля не может быть пустым",
"unmatchedPasswordError": "Пароли не совпадают" "unmatchedPasswordError": "Пароли не совпадают"
}, },
"workspace": { "workspace": {
"create": "Создать рабочее пространство", "create": "Создать рабочее пространство",
"hint": "рабочее пространство", "hint": "рабочее пространство",
"notFoundError": "Нет такого рабочего пространства" "notFoundError": "Нет такого рабочего пространства"
}, },
"shareAction": { "shareAction": {
"buttonText": "Поделиться", "buttonText": "Поделиться",
"workInProgress": "В разработке", "workInProgress": "В разработке",
"markdown": "Markdown", "markdown": "Markdown",
"copyLink": "Скопировать ссылку" "copyLink": "Скопировать ссылку"
}, },
"moreAction": { "moreAction": {
"small": "маленький", "small": "маленький",
"medium": "средний", "medium": "средний",
"large": "большой", "large": "большой",
"fontSize": "Размер шрифта" "fontSize": "Размер шрифта"
}, },
"disclosureAction": { "disclosureAction": {
"rename": "Переименовать", "rename": "Переименовать",
"delete": "Удалить", "delete": "Удалить",
"duplicate": "Дублировать" "duplicate": "Дублировать"
}, },
"blankPageTitle": "Пустая страница", "blankPageTitle": "Пустая страница",
"newPageText": "Новая страница", "newPageText": "Новая страница",
"trash": { "trash": {
"text": "Корзина", "text": "Корзина",
"restoreAll": "Восстановить всё", "restoreAll": "Восстановить всё",
"deleteAll": "Очистить", "deleteAll": "Очистить",
"pageHeader": { "pageHeader": {
"fileName": "Имя", "fileName": "Имя",
"lastModified": "Последнее изменение", "lastModified": "Последнее изменение",
"created": "Создан" "created": "Создан"
} }
}, },
"deletePagePrompt": { "deletePagePrompt": {
"text": "Эта страница в Корзине", "text": "Эта страница в Корзине",
"restore": "Восстановить страницу", "restore": "Восстановить страницу",
"deletePermanent": "Удалить навсегда" "deletePermanent": "Удалить навсегда"
}, },
"dialogCreatePageNameHint": "Имя страницы", "dialogCreatePageNameHint": "Имя страницы",
"questionBubble": { "questionBubble": {
"whatsNew": "Что нового?", "whatsNew": "Что нового?",
"help": "Помощь", "help": "Помощь",
"debug": { "debug": {
"name": "Отладочная информация", "name": "Отладочная информация",
"success": "Скопировано в буфер обмена!", "success": "Скопировано в буфер обмена!",
"fail": "Не получилось скопировать" "fail": "Не получилось скопировать"
} }
}, },
"menuAppHeader": { "menuAppHeader": {
"addPageTooltip": "Быстро добавить новую страницу", "addPageTooltip": "Быстро добавить новую страницу",
"defaultNewPageName": "Без заголовка", "defaultNewPageName": "Без заголовка",
"renameDialog": "Переименовать" "renameDialog": "Переименовать"
}, },
"toolbar": { "toolbar": {
"undo": "Отменить", "undo": "Отменить",
"redo": "Повторить", "redo": "Повторить",
"bold": "Жирный", "bold": "Жирный",
"italic": "Курсив", "italic": "Курсив",
"underline": "Подчёркнутый", "underline": "Подчёркнутый",
"strike": "Зачёркнутый", "strike": "Зачёркнутый",
"numList": "Нумерованный список", "numList": "Нумерованный список",
"bulletList": "Маркированный список", "bulletList": "Маркированный список",
"checkList": "Список To-Do", "checkList": "Список To-Do",
"inlineCode": "Код", "inlineCode": "Код",
"quote": "Цитата", "quote": "Цитата",
"header": "Заголовок", "header": "Заголовок",
"highlight": "Выделение", "highlight": "Выделение",
"color": "Цвет" "color": "Цвет"
}, },
"tooltip": { "tooltip": {
"lightMode": "Переключить на светлую тему", "lightMode": "Переключить на светлую тему",
"darkMode": "Переключить на тёмную тему", "darkMode": "Переключить на тёмную тему",
"openAsPage": "Открыть как страницу", "openAsPage": "Открыть как страницу",
"addNewRow": "Добавить новую строку", "addNewRow": "Добавить новую строку",
"openMenu": "Открыть меню", "openMenu": "Открыть меню",
"viewDataBase": "Просмотр базы данных", "viewDataBase": "Просмотр базы данных",
"referencePage": "Ссылаются на {name}" "referencePage": "Ссылаются на {name}"
}, },
"sideBar": { "sideBar": {
"closeSidebar": "Закрыть боковое меню", "closeSidebar": "Закрыть боковое меню",
"openSidebar": "Открыть боковое меню" "openSidebar": "Открыть боковое меню"
}, },
"notifications": { "notifications": {
"export": { "export": {
"markdown": "Заметка экспортирована в Markdown", "markdown": "Заметка экспортирована в Markdown",
"path": "Документы/flowy" "path": "Документы/flowy"
} }
}, },
"contactsPage": { "contactsPage": {
"title": "Контакты", "title": "Контакты",
"whatsHappening": "Какие события на этой неделе?", "whatsHappening": "Какие события на этой неделе?",
"addContact": "Добавить контакт", "addContact": "Добавить контакт",
"editContact": "Редактировать контакт" "editContact": "Редактировать контакт"
}, },
"button": { "button": {
"OK": "OK", "OK": "OK",
"Cancel": "Отмена", "Cancel": "Отмена",
"signIn": "Войти", "signIn": "Войти",
"signOut": "Выйти", "signOut": "Выйти",
"complete": "Завершить", "complete": "Завершить",
"save": "Сохранить" "save": "Сохранить",
}, "generate": "Сгенерировать",
"label": { "esc": "ESC",
"welcome": "Добро пожаловать!", "keep": "Оставить",
"firstName": "Имя", "tryAgain": "Повторить",
"middleName": "Отчество", "discard": "Отменить",
"lastName": "Фамилия", "replace": "Заменить",
"stepX": "Этап {X}" "insertBelow": "Вставить ниже"
}, },
"oAuth": { "label": {
"err": { "welcome": "Добро пожаловать!",
"failedTitle": "Не удалось подключиться к вашей учетной записи.", "firstName": "Имя",
"failedMsg": "Убедитесь, что вы завершили вход в своём браузере." "middleName": "Отчество",
}, "lastName": "Фамилия",
"google": { "stepX": "Этап {X}"
"title": "Вход через Google", },
"instruction1": "Чтобы импортировать ваши Google Контакты, вам нужно будет авторизовать приложение через браузер.", "oAuth": {
"instruction2": "Скопируйте этот код в буфер обмена (нажав кнопку или выделив текст):", "err": {
"instruction3": "Пройдите по ссылке и введите этот код:", "failedTitle": "Не удалось подключиться к вашей учетной записи.",
"instruction4": "Нажмите на кнопку ниже, когда завершите вход:" "failedMsg": "Убедитесь, что вы завершили вход в своём браузере."
} },
}, "google": {
"settings": { "title": "Вход через Google",
"title": "Настройки", "instruction1": "Чтобы импортировать ваши Google Контакты, вам нужно будет авторизовать приложение через браузер.",
"menu": { "instruction2": "Скопируйте этот код в буфер обмена (нажав кнопку или выделив текст):",
"appearance": "Внешний вид", "instruction3": "Пройдите по ссылке и введите этот код:",
"language": "Язык", "instruction4": "Нажмите на кнопку ниже, когда завершите вход:"
"user": "Пользователь", }
"files": "Файлы", },
"open": "Открыть настройки" "settings": {
}, "title": "Настройки",
"appearance": { "menu": {
"themeMode": { "appearance": "Внешний вид",
"label": "Режим темы", "language": "Язык",
"light": "Светлая", "user": "Пользователь",
"dark": "Тёмная", "files": "Файлы",
"system": "Системная" "open": "Открыть настройки"
}, },
"theme": "Тема" "appearance": {
}, "themeMode": {
"files": { "label": "Режим темы",
"defaultLocation": "Где сейчас хранятся ваши данные", "light": "Светлая",
"doubleTapToCopy": "Нажмите дважды, чтобы скопировать путь", "dark": "Тёмная",
"restoreLocation": "Восстановить путь AppFlowy по умолчанию", "system": "Системная"
"customizeLocation": "Открыть другую папку", },
"restartApp": "Пожалуйста, перезапустите приложение, чтобы изменения вступили в силу.", "theme": "Тема"
"exportDatabase": "Экспорт базы данных", },
"selectFiles": "Выбрать файлы, которые необходимо экспортировать", "files": {
"createNewFolder": "Создать новую папку", "defaultLocation": "Где сейчас хранятся ваши данные",
"createNewFolderDesc": "Указать, где хранить свои данные ...", "doubleTapToCopy": "Нажмите дважды, чтобы скопировать путь",
"open": "Открыть", "restoreLocation": "Восстановить путь AppFlowy по умолчанию",
"openFolder": "Открыть существующую папку", "customizeLocation": "Открыть другую папку",
"openFolderDesc": "Чтение и запись в существующую папку AppFlowy ...", "restartApp": "Пожалуйста, перезапустите приложение, чтобы изменения вступили в силу.",
"folderHintText": "имя папки", "exportDatabase": "Экспорт базы данных",
"location": "Создание новой папки", "selectFiles": "Выбрать файлы, которые необходимо экспортировать",
"locationDesc": "Выбрать имя папки данных AppFlowy", "createNewFolder": "Создать новую папку",
"browser": "Обзор", "createNewFolderDesc": "Указать, где хранить свои данные ...",
"create": "Создать", "open": "Открыть",
"folderPath": "Путь к вашей папке", "openFolder": "Открыть существующую папку",
"locationCannotBeEmpty": "Путь не может быть пустым" "openFolderDesc": "Чтение и запись в существующую папку AppFlowy ...",
} "folderHintText": "имя папки",
}, "location": "Создание новой папки",
"grid": { "locationDesc": "Выбрать имя папки данных AppFlowy",
"settings": { "browser": "Обзор",
"filter": "Фильтр", "create": "Создать",
"sort": "Сортировать", "folderPath": "Путь к вашей папке",
"sortBy": "Сортировать по", "locationCannotBeEmpty": "Путь не может быть пустым",
"Properties": "Свойства", "pathCopiedSnackbar": "File storage path copied to clipboard!"
"group": "Группировать", },
"addFilter": "Добавить фильтр", "user": {
"deleteFilter": "Удалить фильтр", "name": "Имя",
"filterBy": "Фильтровать по...", "icon": "Иконка",
"typeAValue": "Введите значение..." "selectAnIcon": "Выбрать иконку",
}, "pleaseInputYourOpenAIKey": "Введите токен OpenAI"
"textFilter": { }
"contains": "Содержит", },
"doesNotContain": "Не содержит", "grid": {
"endsWith": "Заканчивается на", "settings": {
"startWith": "Начинается с", "filter": "Фильтр",
"is": "Является", "sort": "Сортировать",
"isNot": "Не является", "sortBy": "Сортировать по",
"isEmpty": "Пусто", "Properties": "Свойства",
"isNotEmpty": "Не пусто", "group": "Группировать",
"choicechipPrefix": { "addFilter": "Добавить фильтр",
"isNot": "Не является", "deleteFilter": "Удалить фильтр",
"startWith": "Начинается с", "filterBy": "Фильтровать по...",
"endWith": "Заканчивается на", "typeAValue": "Введите значение..."
"isEmpty": "пусто", },
"isNotEmpty": "не пусто" "textFilter": {
} "contains": "Содержит",
}, "doesNotContain": "Не содержит",
"checkboxFilter": { "endsWith": "Заканчивается на",
"isChecked": "Отмечено", "startWith": "Начинается с",
"isUnchecked": "Не отмечено", "is": "Является",
"choicechipPrefix": { "isNot": "Не является",
"is": "является" "isEmpty": "Пусто",
} "isNotEmpty": "Не пусто",
}, "choicechipPrefix": {
"checklistFilter": { "isNot": "Не является",
"isComplete": "завершено", "startWith": "Начинается с",
"isIncomplted": "не завершено" "endWith": "Заканчивается на",
}, "isEmpty": "пусто",
"singleSelectOptionFilter": { "isNotEmpty": "не пусто"
"is": "Является", }
"isNot": "Не является", },
"isEmpty": "Пусто", "checkboxFilter": {
"isNotEmpty": "Не пусто" "isChecked": "Отмечено",
}, "isUnchecked": "Не отмечено",
"multiSelectOptionFilter": { "choicechipPrefix": {
"contains": "Содержит", "is": "является"
"doesNotContain": "Не содержит", }
"isEmpty": "Пусто", },
"isNotEmpty": "Не пусто" "checklistFilter": {
}, "isComplete": "завершено",
"field": { "isIncomplted": "не завершено"
"hide": "Скрыть", },
"insertLeft": "Вставить слева", "singleSelectOptionFilter": {
"insertRight": "Вставить справа", "is": "Является",
"duplicate": "Дублировать", "isNot": "Не является",
"delete": "Удалить", "isEmpty": "Пусто",
"textFieldName": "Текст", "isNotEmpty": "Не пусто"
"checkboxFieldName": "Чекбокс", },
"dateFieldName": "Дата", "multiSelectOptionFilter": {
"numberFieldName": "Число", "contains": "Содержит",
"singleSelectFieldName": "Выбор", "doesNotContain": "Не содержит",
"multiSelectFieldName": "Выбор нескольких", "isEmpty": "Пусто",
"urlFieldName": "URL", "isNotEmpty": "Не пусто"
"checklistFieldName": "Контрольный список", },
"numberFormat": "Формат числа", "field": {
"dateFormat": "Формат даты", "hide": "Скрыть",
"includeTime": "Время", "insertLeft": "Вставить слева",
"dateFormatFriendly": "День Месяц, Год", "insertRight": "Вставить справа",
"dateFormatISO": "Год-Месяц-День", "duplicate": "Дублировать",
"dateFormatLocal": "Месяц/День/Год", "delete": "Удалить",
"dateFormatUS": "Год/Месяц/День", "textFieldName": "Текст",
"timeFormat": "Форматировать время", "checkboxFieldName": "Чекбокс",
"invalidTimeFormat": "Неверный формат", "dateFieldName": ата",
"timeFormatTwelveHour": "12 часов", "numberFieldName": "Число",
"timeFormatTwentyFourHour": "24 часа", "singleSelectFieldName": "Выбор",
"addSelectOption": "Добавить вариант", "multiSelectFieldName": "Выбор нескольких",
"optionTitle": "Варианты", "urlFieldName": "URL",
"addOption": "Добавить", "checklistFieldName": "Контрольный список",
"editProperty": "Редактировать свойство", "numberFormat": "Формат числа",
"newColumn": "Добавить колонку", "dateFormat": "Формат даты",
"deleteFieldPromptMessage": "Вы уверены? Свойство будет удалено" "includeTime": "Время",
}, "dateFormatFriendly": "День Месяц, Год",
"sort": { "dateFormatISO": "Год-Месяц-День",
"ascending": "По возрастанию", "dateFormatLocal": "Месяц/День/Год",
"descending": "По убыванию", "dateFormatUS": "Год/Месяц/День",
"deleteSort": "Удалить сортировку", "dateFormatDayMonthYear": "День/Mесяц/Год",
"addSort": "Добавить сортировку" "timeFormat": "Формат времени",
}, "invalidTimeFormat": "Неверный формат",
"row": { "timeFormatTwelveHour": "12 часов",
"duplicate": "Дублировать", "timeFormatTwentyFourHour": "24 часа",
"delete": "Удалить", "addSelectOption": "Добавить вариант",
"textPlaceholder": "Пусто", "optionTitle": "Варианты",
"copyProperty": "Свойство скопировано", "addOption": "Добавить",
"count": "Количество", "editProperty": "Редактировать свойство",
"newRow": "Новая строка" "newProperty": "Добавить колонку",
}, "deleteFieldPromptMessage": "Вы уверены? Свойство будет удалено"
"selectOption": { },
"create": "Создать", "sort": {
"purpleColor": "Фиолетовый", "ascending": "По возрастанию",
"pinkColor": "Розовый", "descending": "По убыванию",
"lightPinkColor": "Светло-розовый", "deleteSort": "Удалить сортировку",
"orangeColor": "Оранжевый", "addSort": "Добавить сортировку"
"yellowColor": "Желтый", },
"limeColor": "Ярко-зелёный", "row": {
"greenColor": "Зелёный", "duplicate": "Дублировать",
"aquaColor": "Бирюзовый", "delete": "Удалить",
"blueColor": "Синий", "textPlaceholder": "Пусто",
"deleteTag": "Удалить вариант", "copyProperty": "Свойство скопировано",
"colorPanelTitle": "Цвета", "count": "Количество",
"panelTitle": "Выберите или создайте вариант", "newRow": "Новая строка"
"searchOption": "Поиск" },
}, "selectOption": {
"checklist": { "create": "Создать",
"panelTitle": "Добавить элемент" "purpleColor": "Фиолетовый",
}, "pinkColor": "Розовый",
"menuName": "Сетка" "lightPinkColor": "Светло-розовый",
}, "orangeColor": "Оранжевый",
"document": { "yellowColor": "Желтый",
"menuName": "Документ", "limeColor": "Ярко-зелёный",
"date": { "greenColor": "Зелёный",
"timeHintTextInTwelveHour": "01:00 PM", "aquaColor": "Бирюзовый",
"timeHintTextInTwentyFourHour": "13:00" "blueColor": "Синий",
}, "deleteTag": "Удалить вариант",
"slashMenu": { "colorPanelTitle": "Цвета",
"board": { "panelTitle": "Выберите или создайте вариант",
"selectABoardToLinkTo": "Выбрать доску для связи с" "searchOption": "Поиск"
}, },
"grid": { "checklist": {
"selectAGridToLinkTo": "Выберите сетку для связи с" "panelTitle": "Добавить элемент"
} },
}, "menuName": "Сетка",
"plugins": { "referencedGridPrefix": "Просмотр"
"referencedBoard": "Связанные доски", },
"referencedGrid": "Связанные сетки" "document": {
} "menuName": "Документ",
}, "date": {
"board": { "timeHintTextInTwelveHour": "01:00 PM",
"column": { "timeHintTextInTwentyFourHour": "13:00"
"create_new_card": "Создать" },
}, "slashMenu": {
"menuName": "Доска" "board": {
}, "selectABoardToLinkTo": "Выбрать доску для связи с",
"calendar": { "createANewBoard": "Создать доску"
"menuName": "Календарь", },
"navigation": { "grid": {
"today": "Сегодня", "selectAGridToLinkTo": "Выберите сетку для связи с",
"jumpToday": "Перейти к сегодняшнему дню", "createANewGrid": "Создать сетку"
"previousMonth": "Предыдущий месяц", }
"nextMonth": "Следующий месяц" },
} "plugins": {
} "referencedBoard": "Связанные доски",
} "referencedGrid": "Связанные сетки",
"autoGeneratorMenuItemName": "Генератор OpenAI",
"autoGeneratorTitleName": "OpenAI: попросить ИИ написать что угодно...",
"autoGeneratorLearnMore": "Узнать больше",
"autoGeneratorGenerate": "Генерировать",
"autoGeneratorHintText": "Спросить OpenAI ...",
"autoGeneratorCantGetOpenAIKey": "Не могу получить токен OpenAI",
"smartEdit": "ИИ ассистенты",
"openAI": "OpenAI",
"smartEditFixSpelling": "Исправить правописание",
"warning": "⚠️ Ответы ИИ могут быть неправильными или неточными.",
"smartEditSummarize": "Выделить суть",
"smartEditCouldNotFetchResult": "Не могу получить ответ от OpenAI",
"smartEditCouldNotFetchKey": "Не могу получить токен OpenAI",
"smartEditDisabled": "Подключить OpenAI",
"discardResponse": "Хотите убрать ответы ИИ?",
"cover": {
"changeCover": "Сменить обложку",
"colors": "Цвета",
"images": "Изображения",
"clearAll": "Очистить",
"abstract": "Абстракные",
"addCover": "Добавить обложку",
"addLocalImage": "Добавить изображение с диска",
"invalidImageUrl": "Некорректная ссылка на изображение",
"failedToAddImageToGallery": "Ошибка добавления изображения в галерею",
"enterImageUrl": "Введите ссылку на изображение",
"add": "Добавить",
"back": "Назад",
"saveToGallery": "Сохранить в галерею",
"removeIcon": "Удалить иконку",
"pasteImageUrl": "Вставить ссылку на изображение",
"or": "ИЛИ",
"pickFromFiles": "Выбрать с диска",
"couldNotFetchImage": "Не удалось получить изображение",
"imageSavingFailed": "Не удалось сохранить изображение",
"addIcon": "Добавить иконку",
"coverRemoveAlert": "Изображение будет удалено с обложки",
"alertDialogConfirmation": "Вы хотите продолжить?"
}
}
},
"board": {
"column": {
"create_new_card": "Создать"
},
"menuName": "Доска",
"referencedBoardPrefix": "Просмотр"
},
"calendar": {
"menuName": "Календарь",
"defaultNewCalendarTitle": "Безымянный",
"navigation": {
"today": "Сегодня",
"jumpToday": "Перейти к сегодняшнему дню",
"previousMonth": "Предыдущий месяц",
"nextMonth": "Следующий месяц"
},
"settings": {
"showWeekNumbers": "Показывать номера недель",
"showWeekends": "Показывать выходные",
"firstDayOfWeek": "Первый день недели",
"layoutDateField": "Вид календаря"
}
}
}

View File

@ -195,7 +195,7 @@
"optionTitle": "Alternativ", "optionTitle": "Alternativ",
"addOption": "Lägg till alternativ", "addOption": "Lägg till alternativ",
"editProperty": "Redigera egenskap", "editProperty": "Redigera egenskap",
"newColumn": "Ny kolumn", "newProperty": "Ny kolumn",
"deleteFieldPromptMessage": "Är du säker? Denna egenskap kommer att raderas." "deleteFieldPromptMessage": "Är du säker? Denna egenskap kommer att raderas."
}, },
"row": { "row": {

View File

@ -195,7 +195,7 @@
"optionTitle": "标签", "optionTitle": "标签",
"addOption": "添加标签", "addOption": "添加标签",
"editProperty": "编辑列属性", "editProperty": "编辑列属性",
"newColumn": "增加一列", "newProperty": "增加一列",
"deleteFieldPromptMessage": "确定要删除这个属性吗? " "deleteFieldPromptMessage": "确定要删除这个属性吗? "
}, },
"row": { "row": {

View File

@ -290,7 +290,7 @@
"optionTitle": "選項", "optionTitle": "選項",
"addOption": "新增選項", "addOption": "新增選項",
"editProperty": "編輯內容", "editProperty": "編輯內容",
"newColumn": "新欄位", "newProperty": "新欄位",
"deleteFieldPromptMessage": "你確定嗎?這個內容將被刪除" "deleteFieldPromptMessage": "你確定嗎?這個內容將被刪除"
}, },
"sort": { "sort": {

View File

@ -35,7 +35,9 @@ void main() {
setUpAll(() async => await service.setUpAll()); setUpAll(() async => await service.setUpAll());
setUp(() async => await service.setUp()); setUp(() async => await service.setUp());
testWidgets('integration test unzips the proper workspace and loads it correctly.', (tester) async { testWidgets(
'integration test unzips the proper workspace and loads it correctly.',
(tester) async {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
expect(find.byType(AppFlowyBoard), findsOneWidget); expect(find.byType(AppFlowyBoard), findsOneWidget);
}); });

View File

@ -31,7 +31,8 @@ void main() {
setUpAll(() async => await service.setUpAll()); setUpAll(() async => await service.setUpAll());
setUp(() async => await service.setUp()); setUp(() async => await service.setUp());
testWidgets('/board shortcut creates a new board and view of the board', (tester) async { testWidgets('/board shortcut creates a new board and view of the board',
(tester) async {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
// Needs tab to obtain focus for the app flowy editor. // Needs tab to obtain focus for the app flowy editor.
@ -44,24 +45,30 @@ void main() {
// does not contain any EditableText widgets. // does not contain any EditableText widgets.
// to interact with the app during an integration test, // to interact with the app during an integration test,
// simulate physical keyboard events. // simulate physical keyboard events.
await FlowyTestKeyboard.simulateKeyDownEvent([ await FlowyTestKeyboard.simulateKeyDownEvent(
LogicalKeyboardKey.slash, [
LogicalKeyboardKey.keyB, LogicalKeyboardKey.slash,
LogicalKeyboardKey.keyO, LogicalKeyboardKey.keyB,
LogicalKeyboardKey.keyA, LogicalKeyboardKey.keyO,
LogicalKeyboardKey.keyR, LogicalKeyboardKey.keyA,
LogicalKeyboardKey.keyD, LogicalKeyboardKey.keyR,
LogicalKeyboardKey.arrowDown, LogicalKeyboardKey.keyD,
], tester: tester); LogicalKeyboardKey.arrowDown,
],
tester: tester,
);
// Checks whether the options in the selection menu // Checks whether the options in the selection menu
// for /board exist. // for /board exist.
expect(find.byType(SelectionMenuItemWidget), findsAtLeastNWidgets(2)); expect(find.byType(SelectionMenuItemWidget), findsAtLeastNWidgets(2));
// Finalizes the slash command that creates the board. // Finalizes the slash command that creates the board.
await FlowyTestKeyboard.simulateKeyDownEvent([ await FlowyTestKeyboard.simulateKeyDownEvent(
LogicalKeyboardKey.enter, [
], tester: tester); LogicalKeyboardKey.enter,
],
tester: tester,
);
// Checks whether new board is referenced and properly on the page. // Checks whether new board is referenced and properly on the page.
expect(find.byType(BuiltInPageWidget), findsOneWidget); expect(find.byType(BuiltInPageWidget), findsOneWidget);
@ -75,7 +82,8 @@ void main() {
expect(find.text(viewOfBoardLabel), findsNWidgets(2)); expect(find.text(viewOfBoardLabel), findsNWidgets(2));
}); });
testWidgets('/grid shortcut creates a new grid and view of the grid', (tester) async { testWidgets('/grid shortcut creates a new grid and view of the grid',
(tester) async {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
// Needs tab to obtain focus for the app flowy editor. // Needs tab to obtain focus for the app flowy editor.
@ -88,14 +96,17 @@ void main() {
// does not contain any EditableText widgets. // does not contain any EditableText widgets.
// to interact with the app during an integration test, // to interact with the app during an integration test,
// simulate physical keyboard events. // simulate physical keyboard events.
await FlowyTestKeyboard.simulateKeyDownEvent([ await FlowyTestKeyboard.simulateKeyDownEvent(
LogicalKeyboardKey.slash, [
LogicalKeyboardKey.keyG, LogicalKeyboardKey.slash,
LogicalKeyboardKey.keyR, LogicalKeyboardKey.keyG,
LogicalKeyboardKey.keyI, LogicalKeyboardKey.keyR,
LogicalKeyboardKey.keyD, LogicalKeyboardKey.keyI,
LogicalKeyboardKey.arrowDown, LogicalKeyboardKey.keyD,
], tester: tester); LogicalKeyboardKey.arrowDown,
],
tester: tester,
);
// Checks whether the options in the selection menu // Checks whether the options in the selection menu
// for /grid exist. // for /grid exist.

View File

@ -61,6 +61,8 @@ class TestWorkspaceService {
InputFileStream(await workspace.zip.then((value) => value.path)); InputFileStream(await workspace.zip.then((value) => value.path));
final archive = ZipDecoder().decodeBuffer(inputStream); final archive = ZipDecoder().decodeBuffer(inputStream);
extractArchiveToDisk( extractArchiveToDisk(
archive, await TestWorkspace._parent.then((value) => value.path)); archive,
await TestWorkspace._parent.then((value) => value.path),
);
} }
} }

View File

@ -2,8 +2,10 @@ import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart' as flutter_test; import 'package:flutter_test/flutter_test.dart' as flutter_test;
class FlowyTestKeyboard { class FlowyTestKeyboard {
static Future<void> simulateKeyDownEvent(List<LogicalKeyboardKey> keys, static Future<void> simulateKeyDownEvent(
{required flutter_test.WidgetTester tester}) async { List<LogicalKeyboardKey> keys, {
required flutter_test.WidgetTester tester,
}) async {
for (final LogicalKeyboardKey key in keys) { for (final LogicalKeyboardKey key in keys) {
await flutter_test.simulateKeyDownEvent(key); await flutter_test.simulateKeyDownEvent(key);
await tester.pumpAndSettle(); await tester.pumpAndSettle();

View File

@ -16,9 +16,10 @@ typedef FolderNotificationCallback = void Function(
class FolderNotificationParser class FolderNotificationParser
extends NotificationParser<FolderNotification, FlowyError> { extends NotificationParser<FolderNotification, FlowyError> {
FolderNotificationParser( FolderNotificationParser({
{String? id, required FolderNotificationCallback callback}) String? id,
: super( required FolderNotificationCallback callback,
}) : super(
id: id, id: id,
callback: callback, callback: callback,
tyParser: (ty) => FolderNotification.valueOf(ty), tyParser: (ty) => FolderNotification.valueOf(ty),

View File

@ -16,9 +16,10 @@ typedef DatabaseNotificationCallback = void Function(
class DatabaseNotificationParser class DatabaseNotificationParser
extends NotificationParser<DatabaseNotification, FlowyError> { extends NotificationParser<DatabaseNotification, FlowyError> {
DatabaseNotificationParser( DatabaseNotificationParser({
{String? id, required DatabaseNotificationCallback callback}) String? id,
: super( required DatabaseNotificationCallback callback,
}) : super(
id: id, id: id,
callback: callback, callback: callback,
tyParser: (ty) => DatabaseNotification.valueOf(ty), tyParser: (ty) => DatabaseNotification.valueOf(ty),
@ -27,7 +28,9 @@ class DatabaseNotificationParser
} }
typedef DatabaseNotificationHandler = Function( typedef DatabaseNotificationHandler = Function(
DatabaseNotification ty, Either<Uint8List, FlowyError> result); DatabaseNotification ty,
Either<Uint8List, FlowyError> result,
);
class DatabaseNotificationListener { class DatabaseNotificationListener {
StreamSubscription<SubscribeObject>? _subscription; StreamSubscription<SubscribeObject>? _subscription;

View File

@ -9,11 +9,12 @@ class NotificationParser<T, E> {
T? Function(int) tyParser; T? Function(int) tyParser;
E Function(Uint8List) errorParser; E Function(Uint8List) errorParser;
NotificationParser( NotificationParser({
{this.id, this.id,
required this.callback, required this.callback,
required this.errorParser, required this.errorParser,
required this.tyParser}); required this.tyParser,
});
void parse(SubscribeObject subject) { void parse(SubscribeObject subject) {
if (id != null) { if (id != null) {
if (subject.id != id) { if (subject.id != id) {

View File

@ -16,9 +16,10 @@ typedef UserNotificationCallback = void Function(
class UserNotificationParser class UserNotificationParser
extends NotificationParser<UserNotification, FlowyError> { extends NotificationParser<UserNotification, FlowyError> {
UserNotificationParser( UserNotificationParser({
{required String id, required UserNotificationCallback callback}) required String id,
: super( required UserNotificationCallback callback,
}) : super(
id: id, id: id,
callback: callback, callback: callback,
tyParser: (ty) => UserNotification.valueOf(ty), tyParser: (ty) => UserNotification.valueOf(ty),
@ -27,7 +28,9 @@ class UserNotificationParser
} }
typedef UserNotificationHandler = Function( typedef UserNotificationHandler = Function(
UserNotification ty, Either<Uint8List, FlowyError> result); UserNotification ty,
Either<Uint8List, FlowyError> result,
);
class UserNotificationListener { class UserNotificationListener {
StreamSubscription<SubscribeObject>? _subscription; StreamSubscription<SubscribeObject>? _subscription;

View File

@ -73,28 +73,35 @@ class CellController<T, D> extends Equatable {
/// For example: /// For example:
/// user input: 12 /// user input: 12
/// cell display: $12 /// cell display: $12
_cellListener?.start(onCellChanged: (result) { _cellListener?.start(
result.fold( onCellChanged: (result) {
(_) { result.fold(
_cellCache.remove(_cacheKey); (_) {
_loadData(); _cellCache.remove(_cacheKey);
}, _loadData();
(err) => Log.error(err), },
); (err) => Log.error(err),
}); );
},
);
/// 2.Listen on the field event and load the cell data if needed. /// 2.Listen on the field event and load the cell data if needed.
_fieldListener.start(onFieldChanged: (result) { _fieldListener.start(
result.fold((fieldPB) { onFieldChanged: (result) {
/// reloadOnFieldChanged should be true if you need to load the data when the corresponding field is changed result.fold(
/// For example: (fieldPB) {
/// ¥12 -> $12 /// reloadOnFieldChanged should be true if you need to load the data when the corresponding field is changed
if (_cellDataLoader.reloadOnFieldChanged) { /// For example:
_loadData(); /// ¥12 -> $12
} if (_cellDataLoader.reloadOnFieldChanged) {
_onCellFieldChanged?.call(); _loadData();
}, (err) => Log.error(err)); }
}); _onCellFieldChanged?.call();
},
(err) => Log.error(err),
);
},
);
} }
/// Listen on the cell content or field changes /// Listen on the cell content or field changes
@ -130,7 +137,8 @@ class CellController<T, D> extends Equatable {
/// Return the TypeOptionPB that can be parsed into corresponding class using the [parser]. /// Return the TypeOptionPB that can be parsed into corresponding class using the [parser].
/// [PD] is the type that the parser return. /// [PD] is the type that the parser return.
Future<Either<PD, FlowyError>> getTypeOption<PD, P extends TypeOptionParser>( Future<Either<PD, FlowyError>> getTypeOption<PD, P extends TypeOptionParser>(
P parser) { P parser,
) {
return _fieldBackendSvc return _fieldBackendSvc
.getFieldTypeOptionData(fieldType: fieldType) .getFieldTypeOptionData(fieldType: fieldType)
.then((result) { .then((result) {

View File

@ -19,7 +19,9 @@ class CellListener {
void start({required void Function(UpdateFieldNotifiedValue) onCellChanged}) { void start({required void Function(UpdateFieldNotifiedValue) onCellChanged}) {
_updateCellNotifier?.addPublishListener(onCellChanged); _updateCellNotifier?.addPublishListener(onCellChanged);
_listener = DatabaseNotificationListener( _listener = DatabaseNotificationListener(
objectId: "$rowId:$fieldId", handler: _handler); objectId: "$rowId:$fieldId",
handler: _handler,
);
} }
void _handler(DatabaseNotification ty, Either<Uint8List, FlowyError> result) { void _handler(DatabaseNotification ty, Either<Uint8List, FlowyError> result) {

View File

@ -187,8 +187,10 @@ class DatabaseController {
); );
} }
Future<Either<Unit, FlowyError>> moveGroup( Future<Either<Unit, FlowyError>> moveGroup({
{required String fromGroupId, required String toGroupId}) { required String fromGroupId,
required String toGroupId,
}) {
return _databaseViewBackendSvc.moveGroup( return _databaseViewBackendSvc.moveGroup(
fromGroupId: fromGroupId, fromGroupId: fromGroupId,
toGroupId: toGroupId, toGroupId: toGroupId,
@ -196,7 +198,8 @@ class DatabaseController {
} }
Future<void> updateCalenderLayoutSetting( Future<void> updateCalenderLayoutSetting(
CalendarLayoutSettingsPB layoutSetting) async { CalendarLayoutSettingsPB layoutSetting,
) async {
await _databaseViewBackendSvc await _databaseViewBackendSvc
.updateLayoutSetting(calendarLayoutSetting: layoutSetting) .updateLayoutSetting(calendarLayoutSetting: layoutSetting)
.then((result) { .then((result) {
@ -234,16 +237,20 @@ class DatabaseController {
} }
void _listenOnRowsChanged() { void _listenOnRowsChanged() {
final callbacks = final callbacks = DatabaseViewCallbacks(
DatabaseViewCallbacks(onRowsChanged: (rows, rowByRowId, reason) { onRowsChanged: (rows, rowByRowId, reason) {
_databaseCallbacks?.onRowsChanged?.call(rows, rowByRowId, reason); _databaseCallbacks?.onRowsChanged?.call(rows, rowByRowId, reason);
}, onRowsDeleted: (ids) { },
_databaseCallbacks?.onRowsDeleted?.call(ids); onRowsDeleted: (ids) {
}, onRowsUpdated: (ids) { _databaseCallbacks?.onRowsDeleted?.call(ids);
_databaseCallbacks?.onRowsUpdated?.call(ids); },
}, onRowsCreated: (ids) { onRowsUpdated: (ids) {
_databaseCallbacks?.onRowsCreated?.call(ids); _databaseCallbacks?.onRowsUpdated?.call(ids);
}); },
onRowsCreated: (ids) {
_databaseCallbacks?.onRowsCreated?.call(ids);
},
);
_viewCache.addListener(callbacks); _viewCache.addListener(callbacks);
} }
@ -261,42 +268,58 @@ class DatabaseController {
void _listenOnGroupChanged() { void _listenOnGroupChanged() {
groupListener.start( groupListener.start(
onNumOfGroupsChanged: (result) { onNumOfGroupsChanged: (result) {
result.fold((changeset) { result.fold(
if (changeset.updateGroups.isNotEmpty) { (changeset) {
_groupCallbacks?.onUpdateGroup?.call(changeset.updateGroups); if (changeset.updateGroups.isNotEmpty) {
} _groupCallbacks?.onUpdateGroup?.call(changeset.updateGroups);
}
if (changeset.deletedGroups.isNotEmpty) { if (changeset.deletedGroups.isNotEmpty) {
_groupCallbacks?.onDeleteGroup?.call(changeset.deletedGroups); _groupCallbacks?.onDeleteGroup?.call(changeset.deletedGroups);
} }
for (final insertedGroup in changeset.insertedGroups) { for (final insertedGroup in changeset.insertedGroups) {
_groupCallbacks?.onInsertGroup?.call(insertedGroup); _groupCallbacks?.onInsertGroup?.call(insertedGroup);
} }
}, (r) => Log.error(r)); },
(r) => Log.error(r),
);
}, },
onGroupByNewField: (result) { onGroupByNewField: (result) {
result.fold((groups) { result.fold(
_groupCallbacks?.onGroupByField?.call(groups); (groups) {
}, (r) => Log.error(r)); _groupCallbacks?.onGroupByField?.call(groups);
},
(r) => Log.error(r),
);
}, },
); );
} }
void _listenOnLayoutChanged() { void _listenOnLayoutChanged() {
layoutListener.start(onLayoutChanged: (result) { layoutListener.start(
result.fold((l) { onLayoutChanged: (result) {
_layoutCallbacks?.onLayoutChanged(l); result.fold(
}, (r) => Log.error(r)); (l) {
}); _layoutCallbacks?.onLayoutChanged(l);
},
(r) => Log.error(r),
);
},
);
} }
void _listenOnCalendarLayoutChanged() { void _listenOnCalendarLayoutChanged() {
calendarLayoutListener.start(onCalendarLayoutChanged: (result) { calendarLayoutListener.start(
result.fold((l) { onCalendarLayoutChanged: (result) {
_calendarLayoutCallbacks?.onCalendarLayoutChanged(l); result.fold(
}, (r) => Log.error(r)); (l) {
}); _calendarLayoutCallbacks?.onCalendarLayoutChanged(l);
},
(r) => Log.error(r),
);
},
);
} }
} }

View File

@ -73,8 +73,9 @@ class DatabaseViewBackendService {
return DatabaseEventMoveGroup(payload).send(); return DatabaseEventMoveGroup(payload).send();
} }
Future<Either<List<FieldPB>, FlowyError>> getFields( Future<Either<List<FieldPB>, FlowyError>> getFields({
{List<FieldIdPB>? fieldIds}) { List<FieldIdPB>? fieldIds,
}) {
var payload = GetFieldPayloadPB.create()..viewId = viewId; var payload = GetFieldPayloadPB.create()..viewId = viewId;
if (fieldIds != null) { if (fieldIds != null) {
@ -86,15 +87,17 @@ class DatabaseViewBackendService {
} }
Future<Either<LayoutSettingPB, FlowyError>> getLayoutSetting( Future<Either<LayoutSettingPB, FlowyError>> getLayoutSetting(
DatabaseLayoutPB layoutType) { DatabaseLayoutPB layoutType,
) {
final payload = DatabaseLayoutIdPB.create() final payload = DatabaseLayoutIdPB.create()
..viewId = viewId ..viewId = viewId
..layout = layoutType; ..layout = layoutType;
return DatabaseEventGetLayoutSetting(payload).send(); return DatabaseEventGetLayoutSetting(payload).send();
} }
Future<Either<Unit, FlowyError>> updateLayoutSetting( Future<Either<Unit, FlowyError>> updateLayoutSetting({
{CalendarLayoutSettingsPB? calendarLayoutSetting}) { CalendarLayoutSettingsPB? calendarLayoutSetting,
}) {
final layoutSetting = LayoutSettingPB.create(); final layoutSetting = LayoutSettingPB.create();
if (calendarLayoutSetting != null) { if (calendarLayoutSetting != null) {
layoutSetting.calendar = calendarLayoutSetting; layoutSetting.calendar = calendarLayoutSetting;

View File

@ -17,7 +17,9 @@ class FieldCellBloc extends Bloc<FieldCellEvent, FieldCellState> {
required FieldCellContext cellContext, required FieldCellContext cellContext,
}) : _fieldListener = SingleFieldListener(fieldId: cellContext.field.id), }) : _fieldListener = SingleFieldListener(fieldId: cellContext.field.id),
_fieldBackendSvc = FieldBackendService( _fieldBackendSvc = FieldBackendService(
viewId: cellContext.viewId, fieldId: cellContext.field.id), viewId: cellContext.viewId,
fieldId: cellContext.field.id,
),
super(FieldCellState.initial(cellContext)) { super(FieldCellState.initial(cellContext)) {
on<FieldCellEvent>( on<FieldCellEvent>(
(event, emit) async { (event, emit) async {
@ -49,15 +51,17 @@ class FieldCellBloc extends Bloc<FieldCellEvent, FieldCellState> {
} }
void _startListening() { void _startListening() {
_fieldListener.start(onFieldChanged: (result) { _fieldListener.start(
if (isClosed) { onFieldChanged: (result) {
return; if (isClosed) {
} return;
result.fold( }
(field) => add(FieldCellEvent.didReceiveFieldUpdate(field)), result.fold(
(err) => Log.error(err), (field) => add(FieldCellEvent.didReceiveFieldUpdate(field)),
); (err) => Log.error(err),
}); );
},
);
} }
} }

View File

@ -255,24 +255,26 @@ class FieldController {
} }
} }
_filtersListener.start(onFilterChanged: (result) { _filtersListener.start(
result.fold( onFilterChanged: (result) {
(FilterChangesetNotificationPB changeset) { result.fold(
final List<FilterInfo> filters = filterInfos; (FilterChangesetNotificationPB changeset) {
// Deletes the filters final List<FilterInfo> filters = filterInfos;
deleteFilterFromChangeset(filters, changeset); // Deletes the filters
deleteFilterFromChangeset(filters, changeset);
// Inserts the new filter if it's not exist // Inserts the new filter if it's not exist
insertFilterFromChangeset(filters, changeset); insertFilterFromChangeset(filters, changeset);
updateFilterFromChangeset(filters, changeset); updateFilterFromChangeset(filters, changeset);
_updateFieldInfos(); _updateFieldInfos();
_filterNotifier?.filters = filters; _filterNotifier?.filters = filters;
}, },
(err) => Log.error(err), (err) => Log.error(err),
); );
}); },
);
} }
void _listenOnSortChanged() { void _listenOnSortChanged() {
@ -347,48 +349,54 @@ class FieldController {
} }
} }
_sortsListener.start(onSortChanged: (result) { _sortsListener.start(
result.fold( onSortChanged: (result) {
(SortChangesetNotificationPB changeset) { result.fold(
final List<SortInfo> newSortInfos = sortInfos; (SortChangesetNotificationPB changeset) {
deleteSortFromChangeset(newSortInfos, changeset); final List<SortInfo> newSortInfos = sortInfos;
insertSortFromChangeset(newSortInfos, changeset); deleteSortFromChangeset(newSortInfos, changeset);
updateSortFromChangeset(newSortInfos, changeset); insertSortFromChangeset(newSortInfos, changeset);
updateSortFromChangeset(newSortInfos, changeset);
_updateFieldInfos(); _updateFieldInfos();
_sortNotifier?.sorts = newSortInfos; _sortNotifier?.sorts = newSortInfos;
}, },
(err) => Log.error(err), (err) => Log.error(err),
); );
}); },
);
} }
void _listenOnSettingChanges() { void _listenOnSettingChanges() {
//Listen on setting changes //Listen on setting changes
_settingListener.start(onSettingUpdated: (result) { _settingListener.start(
result.fold( onSettingUpdated: (result) {
(setting) => _updateSetting(setting), result.fold(
(r) => Log.error(r), (setting) => _updateSetting(setting),
); (r) => Log.error(r),
}); );
},
);
} }
void _listenOnFieldChanges() { void _listenOnFieldChanges() {
//Listen on field's changes //Listen on field's changes
_fieldListener.start(onFieldsChanged: (result) { _fieldListener.start(
result.fold( onFieldsChanged: (result) {
(changeset) { result.fold(
_deleteFields(changeset.deletedFields); (changeset) {
_insertFields(changeset.insertedFields); _deleteFields(changeset.deletedFields);
_insertFields(changeset.insertedFields);
final updatedFields = _updateFields(changeset.updatedFields); final updatedFields = _updateFields(changeset.updatedFields);
for (final listener in _updatedFieldCallbacks.values) { for (final listener in _updatedFieldCallbacks.values) {
listener(updatedFields); listener(updatedFields);
} }
}, },
(err) => Log.error(err), (err) => Log.error(err),
); );
}); },
);
} }
void _updateSetting(DatabaseViewSettingPB setting) { void _updateSetting(DatabaseViewSettingPB setting) {

View File

@ -36,11 +36,13 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
} }
}, },
didReceiveFieldChanged: (FieldPB field) { didReceiveFieldChanged: (FieldPB field) {
emit(state.copyWith( emit(
field: Some(field), state.copyWith(
name: field.name, field: Some(field),
canDelete: field.isPrimary, name: field.name,
)); canDelete: field.isPrimary,
),
);
}, },
deleteField: () { deleteField: () {
state.field.fold( state.field.fold(

View File

@ -17,8 +17,9 @@ class SingleFieldListener {
SingleFieldListener({required this.fieldId}); SingleFieldListener({required this.fieldId});
void start( void start({
{required void Function(UpdateFieldNotifiedValue) onFieldChanged}) { required void Function(UpdateFieldNotifiedValue) onFieldChanged,
}) {
_updateFieldNotifier?.addPublishListener(onFieldChanged); _updateFieldNotifier?.addPublishListener(onFieldChanged);
_listener = DatabaseNotificationListener( _listener = DatabaseNotificationListener(
objectId: fieldId, objectId: fieldId,
@ -60,8 +61,9 @@ class FieldsListener {
DatabaseNotificationListener? _listener; DatabaseNotificationListener? _listener;
FieldsListener({required this.viewId}); FieldsListener({required this.viewId});
void start( void start({
{required void Function(UpdateFieldsNotifiedValue) onFieldsChanged}) { required void Function(UpdateFieldsNotifiedValue) onFieldsChanged,
}) {
updateFieldsNotifier?.addPublishListener(onFieldsChanged); updateFieldsNotifier?.addPublishListener(onFieldsChanged);
_listener = DatabaseNotificationListener( _listener = DatabaseNotificationListener(
objectId: viewId, objectId: viewId,

View File

@ -15,16 +15,25 @@ class DateTypeOptionBloc
(event, emit) async { (event, emit) async {
event.map( event.map(
didSelectDateFormat: (_DidSelectDateFormat value) { didSelectDateFormat: (_DidSelectDateFormat value) {
emit(state.copyWith( emit(
typeOption: _updateTypeOption(dateFormat: value.format))); state.copyWith(
typeOption: _updateTypeOption(dateFormat: value.format),
),
);
}, },
didSelectTimeFormat: (_DidSelectTimeFormat value) { didSelectTimeFormat: (_DidSelectTimeFormat value) {
emit(state.copyWith( emit(
typeOption: _updateTypeOption(timeFormat: value.format))); state.copyWith(
typeOption: _updateTypeOption(timeFormat: value.format),
),
);
}, },
includeTime: (_IncludeTime value) { includeTime: (_IncludeTime value) {
emit(state.copyWith( emit(
typeOption: _updateTypeOption(includeTime: value.includeTime))); state.copyWith(
typeOption: _updateTypeOption(includeTime: value.includeTime),
),
);
}, },
); );
}, },

View File

@ -7,16 +7,20 @@ class NumberFormatBloc extends Bloc<NumberFormatEvent, NumberFormatState> {
NumberFormatBloc() : super(NumberFormatState.initial()) { NumberFormatBloc() : super(NumberFormatState.initial()) {
on<NumberFormatEvent>( on<NumberFormatEvent>(
(event, emit) async { (event, emit) async {
event.map(setFilter: (_SetFilter value) { event.map(
final List<NumberFormat> formats = List.from(NumberFormat.values); setFilter: (_SetFilter value) {
if (value.filter.isNotEmpty) { final List<NumberFormat> formats = List.from(NumberFormat.values);
formats.retainWhere((element) => element if (value.filter.isNotEmpty) {
.title() formats.retainWhere(
.toLowerCase() (element) => element
.contains(value.filter.toLowerCase())); .title()
} .toLowerCase()
emit(state.copyWith(formats: formats, filter: value.filter)); .contains(value.filter.toLowerCase()),
}); );
}
emit(state.copyWith(formats: formats, filter: value.filter));
},
);
}, },
); );
} }

View File

@ -59,9 +59,11 @@ class SelectOptionTypeOptionEvent with _$SelectOptionTypeOptionEvent {
const factory SelectOptionTypeOptionEvent.endAddingOption() = const factory SelectOptionTypeOptionEvent.endAddingOption() =
_EndAddingOption; _EndAddingOption;
const factory SelectOptionTypeOptionEvent.updateOption( const factory SelectOptionTypeOptionEvent.updateOption(
SelectOptionPB option) = _UpdateOption; SelectOptionPB option,
) = _UpdateOption;
const factory SelectOptionTypeOptionEvent.deleteOption( const factory SelectOptionTypeOptionEvent.deleteOption(
SelectOptionPB option) = _DeleteOption; SelectOptionPB option,
) = _DeleteOption;
} }
@freezed @freezed

View File

@ -158,7 +158,9 @@ abstract class IFieldTypeOptionLoader {
Future<Either<TypeOptionPB, FlowyError>> load(); Future<Either<TypeOptionPB, FlowyError>> load();
Future<Either<Unit, FlowyError>> switchToField( Future<Either<Unit, FlowyError>> switchToField(
String fieldId, FieldType fieldType) { String fieldId,
FieldType fieldType,
) {
final payload = UpdateFieldTypePayloadPB.create() final payload = UpdateFieldTypePayloadPB.create()
..viewId = viewId ..viewId = viewId
..fieldId = fieldId ..fieldId = fieldId

View File

@ -107,7 +107,8 @@ class FilterListener {
case DatabaseNotification.DidUpdateFilter: case DatabaseNotification.DidUpdateFilter:
result.fold( result.fold(
(payload) => handleChangeset( (payload) => handleChangeset(
FilterChangesetNotificationPB.fromBuffer(payload)), FilterChangesetNotificationPB.fromBuffer(payload),
),
(error) {}, (error) {},
); );
break; break;

View File

@ -97,7 +97,8 @@ class FilterBackendService {
filter.end = $fixnum.Int64(end); filter.end = $fixnum.Int64(end);
} else { } else {
throw Exception( throw Exception(
"Start and end should not be null if the timestamp is null"); "Start and end should not be null if the timestamp is null",
);
} }
} }

View File

@ -15,8 +15,9 @@ class DatabaseCalendarLayoutListener {
DatabaseNotificationListener? _listener; DatabaseNotificationListener? _listener;
DatabaseCalendarLayoutListener(this.viewId); DatabaseCalendarLayoutListener(this.viewId);
void start( void start({
{required void Function(NewLayoutFieldValue) onCalendarLayoutChanged}) { required void Function(NewLayoutFieldValue) onCalendarLayoutChanged,
}) {
_newLayoutFieldNotifier?.addPublishListener(onCalendarLayoutChanged); _newLayoutFieldNotifier?.addPublishListener(onCalendarLayoutChanged);
_listener = DatabaseNotificationListener( _listener = DatabaseNotificationListener(
objectId: viewId, objectId: viewId,

View File

@ -102,11 +102,16 @@ class RowCache {
final rowInfo = _rowList.get(reorderRow.rowId); final rowInfo = _rowList.get(reorderRow.rowId);
if (rowInfo != null) { if (rowInfo != null) {
_rowList.moveRow( _rowList.moveRow(
reorderRow.rowId, reorderRow.oldIndex, reorderRow.newIndex); reorderRow.rowId,
_rowChangeReasonNotifier.receive(RowsChangedReason.reorderSingleRow( reorderRow.oldIndex,
reorderRow, reorderRow.newIndex,
rowInfo, );
)); _rowChangeReasonNotifier.receive(
RowsChangedReason.reorderSingleRow(
reorderRow,
rowInfo,
),
);
} }
} }
@ -325,7 +330,9 @@ class RowsChangedReason with _$RowsChangedReason {
const factory RowsChangedReason.initial() = InitialListState; const factory RowsChangedReason.initial() = InitialListState;
const factory RowsChangedReason.reorderRows() = _ReorderRows; const factory RowsChangedReason.reorderRows() = _ReorderRows;
const factory RowsChangedReason.reorderSingleRow( const factory RowsChangedReason.reorderSingleRow(
ReorderSingleRowPB reorderRow, RowInfo rowInfo) = _ReorderSingleRow; ReorderSingleRowPB reorderRow,
RowInfo rowInfo,
) = _ReorderSingleRow;
} }
class InsertedIndex { class InsertedIndex {

View File

@ -23,10 +23,12 @@ class RowController {
} }
void addListener({OnRowChanged? onRowChanged}) { void addListener({OnRowChanged? onRowChanged}) {
_onRowChangedListeners.add(_rowCache.addListener( _onRowChangedListeners.add(
rowId: rowId, _rowCache.addListener(
onCellUpdated: onRowChanged, rowId: rowId,
)); onCellUpdated: onRowChanged,
),
);
} }
void dispose() { void dispose() {

View File

@ -85,10 +85,12 @@ class RowList {
insert(index, builder(insertRow.row)); insert(index, builder(insertRow.row));
if (!isContains) { if (!isContains) {
insertIndexs.add(InsertedIndex( insertIndexs.add(
index: index, InsertedIndex(
rowId: insertRow.row.id, index: index,
)); rowId: insertRow.row.id,
),
);
} }
} }
return insertIndexs; return insertIndexs;

View File

@ -67,7 +67,8 @@ class DatabaseGroupEvent with _$DatabaseGroupEvent {
FieldType fieldType, FieldType fieldType,
) = _DatabaseGroupEvent; ) = _DatabaseGroupEvent;
const factory DatabaseGroupEvent.didReceiveFieldUpdate( const factory DatabaseGroupEvent.didReceiveFieldUpdate(
List<FieldInfo> fields) = _DidReceiveFieldUpdate; List<FieldInfo> fields,
) = _DidReceiveFieldUpdate;
} }
@freezed @freezed
@ -78,7 +79,9 @@ class DatabaseGroupState with _$DatabaseGroupState {
}) = _DatabaseGroupState; }) = _DatabaseGroupState;
factory DatabaseGroupState.initial( factory DatabaseGroupState.initial(
String viewId, List<FieldInfo> fieldContexts) => String viewId,
List<FieldInfo> fieldContexts,
) =>
DatabaseGroupState( DatabaseGroupState(
viewId: viewId, viewId: viewId,
fieldContexts: fieldContexts, fieldContexts: fieldContexts,

View File

@ -13,11 +13,13 @@ class DatabasePropertyBloc
final FieldController _fieldController; final FieldController _fieldController;
Function(List<FieldInfo>)? _onFieldsFn; Function(List<FieldInfo>)? _onFieldsFn;
DatabasePropertyBloc( DatabasePropertyBloc({
{required String viewId, required FieldController fieldController}) required String viewId,
: _fieldController = fieldController, required FieldController fieldController,
}) : _fieldController = fieldController,
super( super(
DatabasePropertyState.initial(viewId, fieldController.fieldInfos)) { DatabasePropertyState.initial(viewId, fieldController.fieldInfos),
) {
on<DatabasePropertyEvent>( on<DatabasePropertyEvent>(
(event, emit) async { (event, emit) async {
await event.map( await event.map(
@ -68,9 +70,12 @@ class DatabasePropertyBloc
class DatabasePropertyEvent with _$DatabasePropertyEvent { class DatabasePropertyEvent with _$DatabasePropertyEvent {
const factory DatabasePropertyEvent.initial() = _Initial; const factory DatabasePropertyEvent.initial() = _Initial;
const factory DatabasePropertyEvent.setFieldVisibility( const factory DatabasePropertyEvent.setFieldVisibility(
String fieldId, bool visibility) = _SetFieldVisibility; String fieldId,
bool visibility,
) = _SetFieldVisibility;
const factory DatabasePropertyEvent.didReceiveFieldUpdate( const factory DatabasePropertyEvent.didReceiveFieldUpdate(
List<FieldInfo> fields) = _DidReceiveFieldUpdate; List<FieldInfo> fields,
) = _DidReceiveFieldUpdate;
const factory DatabasePropertyEvent.moveField(int fromIndex, int toIndex) = const factory DatabasePropertyEvent.moveField(int fromIndex, int toIndex) =
_MoveField; _MoveField;
} }

View File

@ -11,9 +11,11 @@ class DatabaseSettingBloc
: super(DatabaseSettingState.initial()) { : super(DatabaseSettingState.initial()) {
on<DatabaseSettingEvent>( on<DatabaseSettingEvent>(
(event, emit) async { (event, emit) async {
event.map(performAction: (_PerformAction value) { event.map(
emit(state.copyWith(selectedAction: Some(value.action))); performAction: (_PerformAction value) {
}); emit(state.copyWith(selectedAction: Some(value.action)));
},
);
}, },
); );
} }
@ -22,7 +24,8 @@ class DatabaseSettingBloc
@freezed @freezed
class DatabaseSettingEvent with _$DatabaseSettingEvent { class DatabaseSettingEvent with _$DatabaseSettingEvent {
const factory DatabaseSettingEvent.performAction( const factory DatabaseSettingEvent.performAction(
DatabaseSettingAction action) = _PerformAction; DatabaseSettingAction action,
) = _PerformAction;
} }
@freezed @freezed

View File

@ -28,12 +28,14 @@ class SettingController {
}); });
// Listen on the setting changes // Listen on the setting changes
_listener.start(onSettingUpdated: (result) { _listener.start(
result.fold( onSettingUpdated: (result) {
(newSetting) => updateSetting(newSetting), result.fold(
(err) => _onError?.call(err), (newSetting) => updateSetting(newSetting),
); (err) => _onError?.call(err),
}); );
},
);
} }
void startListening({ void startListening({

View File

@ -69,9 +69,11 @@ class DatabaseViewCache {
} }
if (changeset.insertedRows.isNotEmpty) { if (changeset.insertedRows.isNotEmpty) {
_callbacks?.onRowsCreated?.call(changeset.insertedRows _callbacks?.onRowsCreated?.call(
.map((insertedRow) => insertedRow.row.id) changeset.insertedRows
.toList()); .map((insertedRow) => insertedRow.row.id)
.toList(),
);
} }
}, },
(err) => Log.error(err), (err) => Log.error(err),

View File

@ -105,23 +105,31 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
); );
}, },
didCreateRow: (group, row, int? index) { didCreateRow: (group, row, int? index) {
emit(state.copyWith( emit(
editingRow: Some(BoardEditingRow( state.copyWith(
group: group, editingRow: Some(
row: row, BoardEditingRow(
index: index, group: group,
)), row: row,
)); index: index,
),
),
),
);
_groupItemStartEditing(group, row, true); _groupItemStartEditing(group, row, true);
}, },
startEditingRow: (group, row) { startEditingRow: (group, row) {
emit(state.copyWith( emit(
editingRow: Some(BoardEditingRow( state.copyWith(
group: group, editingRow: Some(
row: row, BoardEditingRow(
index: null, group: group,
)), row: row,
)); index: null,
),
),
),
);
_groupItemStartEditing(group, row, true); _groupItemStartEditing(group, row, true);
}, },
endEditingRow: (rowId) { endEditingRow: (rowId) {
@ -175,10 +183,12 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
groupControllers.clear(); groupControllers.clear();
boardController.clear(); boardController.clear();
boardController.addGroups(groups boardController.addGroups(
.where((group) => fieldController.getField(group.fieldId) != null) groups
.map((group) => initializeGroupData(group)) .where((group) => fieldController.getField(group.fieldId) != null)
.toList()); .map((group) => initializeGroupData(group))
.toList(),
);
for (final group in groups) { for (final group in groups) {
final controller = initializeGroupController(group); final controller = initializeGroupController(group);
@ -334,7 +344,8 @@ class BoardState with _$BoardState {
class GridLoadingState with _$GridLoadingState { class GridLoadingState with _$GridLoadingState {
const factory GridLoadingState.loading() = _Loading; const factory GridLoadingState.loading() = _Loading;
const factory GridLoadingState.finish( const factory GridLoadingState.finish(
Either<Unit, FlowyError> successOrFail) = _Finish; Either<Unit, FlowyError> successOrFail,
) = _Finish;
} }
class GridFieldEquatable extends Equatable { class GridFieldEquatable extends Equatable {

View File

@ -41,44 +41,46 @@ class GroupController {
} }
void startListening() { void startListening() {
_listener.start(onGroupChanged: (result) { _listener.start(
result.fold( onGroupChanged: (result) {
(GroupRowsNotificationPB changeset) { result.fold(
for (final deletedRow in changeset.deletedRows) { (GroupRowsNotificationPB changeset) {
group.rows.removeWhere((rowPB) => rowPB.id == deletedRow); for (final deletedRow in changeset.deletedRows) {
delegate.removeRow(group, deletedRow); group.rows.removeWhere((rowPB) => rowPB.id == deletedRow);
} delegate.removeRow(group, deletedRow);
for (final insertedRow in changeset.insertedRows) {
final index = insertedRow.hasIndex() ? insertedRow.index : null;
if (insertedRow.hasIndex() &&
group.rows.length > insertedRow.index) {
group.rows.insert(insertedRow.index, insertedRow.row);
} else {
group.rows.add(insertedRow.row);
} }
if (insertedRow.isNew) { for (final insertedRow in changeset.insertedRows) {
delegate.addNewRow(group, insertedRow.row, index); final index = insertedRow.hasIndex() ? insertedRow.index : null;
} else { if (insertedRow.hasIndex() &&
delegate.insertRow(group, insertedRow.row, index); group.rows.length > insertedRow.index) {
} group.rows.insert(insertedRow.index, insertedRow.row);
} } else {
group.rows.add(insertedRow.row);
}
for (final updatedRow in changeset.updatedRows) { if (insertedRow.isNew) {
final index = group.rows.indexWhere( delegate.addNewRow(group, insertedRow.row, index);
(rowPB) => rowPB.id == updatedRow.id, } else {
); delegate.insertRow(group, insertedRow.row, index);
}
if (index != -1) {
group.rows[index] = updatedRow;
delegate.updateRow(group, updatedRow);
} }
}
}, for (final updatedRow in changeset.updatedRows) {
(err) => Log.error(err), final index = group.rows.indexWhere(
); (rowPB) => rowPB.id == updatedRow.id,
}); );
if (index != -1) {
group.rows[index] = updatedRow;
delegate.updateRow(group, updatedRow);
}
}
},
(err) => Log.error(err),
);
},
);
} }
Future<void> dispose() async { Future<void> dispose() async {

View File

@ -10,9 +10,11 @@ class BoardSettingBloc extends Bloc<BoardSettingEvent, BoardSettingState> {
: super(BoardSettingState.initial()) { : super(BoardSettingState.initial()) {
on<BoardSettingEvent>( on<BoardSettingEvent>(
(event, emit) async { (event, emit) async {
event.when(performAction: (action) { event.when(
emit(state.copyWith(selectedAction: Some(action))); performAction: (action) {
}); emit(state.copyWith(selectedAction: Some(action)));
},
);
}, },
); );
} }

View File

@ -80,8 +80,8 @@ class _BoardContentState extends State<BoardContent> {
late AppFlowyBoardScrollController scrollManager; late AppFlowyBoardScrollController scrollManager;
final cardConfiguration = CardConfiguration<String>(); final cardConfiguration = CardConfiguration<String>();
final config = AppFlowyBoardConfig( final config = const AppFlowyBoardConfig(
groupBackgroundColor: HexColor.fromHex('#F7F8FC'), groupBackgroundColor: Color(0xffF7F8FC),
); );
@override @override
@ -292,9 +292,10 @@ class _BoardContentState extends State<BoardContent> {
color: Theme.of(context).dividerColor, color: Theme.of(context).dividerColor,
width: 1.0, width: 1.0,
); );
final isLightMode = Theme.of(context).brightness == Brightness.light;
return BoxDecoration( return BoxDecoration(
color: Theme.of(context).colorScheme.surface, color: Theme.of(context).colorScheme.surface,
border: Border.fromBorderSide(borderSide), border: isLightMode ? Border.fromBorderSide(borderSide) : null,
borderRadius: const BorderRadius.all(Radius.circular(6)), borderRadius: const BorderRadius.all(Radius.circular(6)),
); );
} }
@ -349,15 +350,6 @@ class _ToolbarBlocAdaptor extends StatelessWidget {
} }
} }
extension HexColor on Color {
static Color fromHex(String hexString) {
final buffer = StringBuffer();
if (hexString.length == 6 || hexString.length == 7) buffer.write('ff');
buffer.write(hexString.replaceFirst('#', ''));
return Color(int.parse(buffer.toString(), radix: 16));
}
}
Widget? _buildHeaderIcon(GroupData customData) { Widget? _buildHeaderIcon(GroupData customData) {
Widget? widget; Widget? widget;
switch (customData.fieldType) { switch (customData.fieldType) {

View File

@ -7,6 +7,7 @@ import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar
import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/style_widget/button.dart'; import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart'; import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/style_widget/text.dart';
@ -104,8 +105,12 @@ class _SettingItem extends StatelessWidget {
return SizedBox( return SizedBox(
height: 30, height: 30,
child: FlowyButton( child: FlowyButton(
hoverColor: AFThemeExtension.of(context).lightGreyHover,
isSelected: isSelected, isSelected: isSelected,
text: FlowyText.medium(action.title()), text: FlowyText.medium(
action.title(),
color: AFThemeExtension.of(context).textColor,
),
onTap: () { onTap: () {
context context
.read<BoardSettingBloc>() .read<BoardSettingBloc>()

View File

@ -71,6 +71,7 @@ class _SettingButtonState extends State<_SettingButton> {
margin: EdgeInsets.zero, margin: EdgeInsets.zero,
child: FlowyTextButton( child: FlowyTextButton(
LocaleKeys.settings_title.tr(), LocaleKeys.settings_title.tr(),
fontColor: AFThemeExtension.of(context).textColor,
fillColor: Colors.transparent, fillColor: Colors.transparent,
hoverColor: AFThemeExtension.of(context).lightGreyHover, hoverColor: AFThemeExtension.of(context).lightGreyHover,
padding: GridSize.typeOptionContentInsets, padding: GridSize.typeOptionContentInsets,

View File

@ -67,16 +67,20 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
if (index != -1) { if (index != -1) {
allEvents[index] = eventData; allEvents[index] = eventData;
} }
emit(state.copyWith( emit(
allEvents: allEvents, state.copyWith(
updateEvent: eventData, allEvents: allEvents,
)); updateEvent: eventData,
),
);
}, },
didReceiveNewEvent: (CalendarEventData<CalendarDayEvent> event) { didReceiveNewEvent: (CalendarEventData<CalendarDayEvent> event) {
emit(state.copyWith( emit(
allEvents: [...state.allEvents, event], state.copyWith(
newEvent: event, allEvents: [...state.allEvents, event],
)); newEvent: event,
),
);
}, },
didDeleteEvents: (List<String> deletedRowIds) { didDeleteEvents: (List<String> deletedRowIds) {
var events = [...state.allEvents]; var events = [...state.allEvents];
@ -155,7 +159,8 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
} }
Future<void> _updateCalendarLayoutSetting( Future<void> _updateCalendarLayoutSetting(
CalendarLayoutSettingsPB layoutSetting) async { CalendarLayoutSettingsPB layoutSetting,
) async {
return _databaseController.updateCalenderLayoutSetting(layoutSetting); return _databaseController.updateCalenderLayoutSetting(layoutSetting);
} }
@ -198,7 +203,8 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
} }
CalendarEventData<CalendarDayEvent>? _calendarEventDataFromEventPB( CalendarEventData<CalendarDayEvent>? _calendarEventDataFromEventPB(
CalendarEventPB eventPB) { CalendarEventPB eventPB,
) {
final fieldInfo = fieldInfoByFieldId[eventPB.titleFieldId]; final fieldInfo = fieldInfoByFieldId[eventPB.titleFieldId];
if (fieldInfo != null) { if (fieldInfo != null) {
final cellId = CellIdentifier( final cellId = CellIdentifier(
@ -214,7 +220,6 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
final date = DateTime.fromMillisecondsSinceEpoch( final date = DateTime.fromMillisecondsSinceEpoch(
eventPB.timestamp.toInt() * 1000, eventPB.timestamp.toInt() * 1000,
isUtc: true,
); );
return CalendarEventData( return CalendarEventData(
title: eventPB.title, title: eventPB.title,
@ -267,7 +272,8 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
); );
final onCalendarLayoutFieldChanged = CalendarLayoutCallbacks( final onCalendarLayoutFieldChanged = CalendarLayoutCallbacks(
onCalendarLayoutChanged: _didReceiveNewLayoutField); onCalendarLayoutChanged: _didReceiveNewLayoutField,
);
_databaseController.addListener( _databaseController.addListener(
onDatabaseChanged: onDatabaseChanged, onDatabaseChanged: onDatabaseChanged,
@ -299,7 +305,8 @@ class CalendarEvent with _$CalendarEvent {
// Called after loading the calendar layout setting from the backend // Called after loading the calendar layout setting from the backend
const factory CalendarEvent.didReceiveCalendarSettings( const factory CalendarEvent.didReceiveCalendarSettings(
CalendarLayoutSettingsPB settings) = _ReceiveCalendarSettings; CalendarLayoutSettingsPB settings,
) = _ReceiveCalendarSettings;
// Called after loading all the current evnets // Called after loading all the current evnets
const factory CalendarEvent.didLoadAllEvents(Events events) = const factory CalendarEvent.didLoadAllEvents(Events events) =
@ -307,11 +314,13 @@ class CalendarEvent with _$CalendarEvent {
// Called when specific event was updated // Called when specific event was updated
const factory CalendarEvent.didUpdateEvent( const factory CalendarEvent.didUpdateEvent(
CalendarEventData<CalendarDayEvent> event) = _DidUpdateEvent; CalendarEventData<CalendarDayEvent> event,
) = _DidUpdateEvent;
// Called after creating a new event // Called after creating a new event
const factory CalendarEvent.didReceiveNewEvent( const factory CalendarEvent.didReceiveNewEvent(
CalendarEventData<CalendarDayEvent> event) = _DidReceiveNewEvent; CalendarEventData<CalendarDayEvent> event,
) = _DidReceiveNewEvent;
// Called when deleting events // Called when deleting events
const factory CalendarEvent.didDeleteEvents(List<String> rowIds) = const factory CalendarEvent.didDeleteEvents(List<String> rowIds) =
@ -323,13 +332,15 @@ class CalendarEvent with _$CalendarEvent {
// Called when updating the calendar's layout settings // Called when updating the calendar's layout settings
const factory CalendarEvent.updateCalendarLayoutSetting( const factory CalendarEvent.updateCalendarLayoutSetting(
CalendarLayoutSettingsPB layoutSetting) = _UpdateCalendarLayoutSetting; CalendarLayoutSettingsPB layoutSetting,
) = _UpdateCalendarLayoutSetting;
const factory CalendarEvent.didReceiveDatabaseUpdate(DatabasePB database) = const factory CalendarEvent.didReceiveDatabaseUpdate(DatabasePB database) =
_ReceiveDatabaseUpdate; _ReceiveDatabaseUpdate;
const factory CalendarEvent.didReceiveNewLayoutField( const factory CalendarEvent.didReceiveNewLayoutField(
CalendarLayoutSettingsPB layoutSettings) = _DidReceiveNewLayoutField; CalendarLayoutSettingsPB layoutSettings,
) = _DidReceiveNewLayoutField;
} }
@freezed @freezed
@ -361,7 +372,8 @@ class CalendarState with _$CalendarState {
class DatabaseLoadingState with _$DatabaseLoadingState { class DatabaseLoadingState with _$DatabaseLoadingState {
const factory DatabaseLoadingState.loading() = _Loading; const factory DatabaseLoadingState.loading() = _Loading;
const factory DatabaseLoadingState.finish( const factory DatabaseLoadingState.finish(
Either<Unit, FlowyError> successOrFail) = _Finish; Either<Unit, FlowyError> successOrFail,
) = _Finish;
} }
class CalendarEditingRow { class CalendarEditingRow {

View File

@ -22,7 +22,6 @@ class CalendarSettingBloc
); );
}); });
} }
} }
@freezed @freezed
@ -33,7 +32,8 @@ class CalendarSettingState with _$CalendarSettingState {
}) = _CalendarSettingState; }) = _CalendarSettingState;
factory CalendarSettingState.initial( factory CalendarSettingState.initial(
CalendarLayoutSettingsPB? layoutSettings) => CalendarLayoutSettingsPB? layoutSettings,
) =>
CalendarSettingState( CalendarSettingState(
selectedAction: none(), selectedAction: none(),
layoutSetting: layoutSettings == null ? none() : Some(layoutSettings), layoutSetting: layoutSettings == null ? none() : Some(layoutSettings),
@ -43,9 +43,11 @@ class CalendarSettingState with _$CalendarSettingState {
@freezed @freezed
class CalendarSettingEvent with _$CalendarSettingEvent { class CalendarSettingEvent with _$CalendarSettingEvent {
const factory CalendarSettingEvent.performAction( const factory CalendarSettingEvent.performAction(
CalendarSettingAction action) = _PerformAction; CalendarSettingAction action,
) = _PerformAction;
const factory CalendarSettingEvent.updateLayoutSetting( const factory CalendarSettingEvent.updateLayoutSetting(
CalendarLayoutSettingsPB setting) = _UpdateLayoutSetting; CalendarLayoutSettingsPB setting,
) = _UpdateLayoutSetting;
} }
enum CalendarSettingAction { enum CalendarSettingAction {

View File

@ -62,30 +62,32 @@ class CalendarDayCard extends StatelessWidget {
); );
}).toList(); }).toList();
final child = Padding( final child = Column(
padding: const EdgeInsets.all(8.0), mainAxisSize: MainAxisSize.min,
child: Column( children: [
mainAxisSize: MainAxisSize.min, Padding(
children: [ padding: const EdgeInsets.all(8.0),
_Header( child: _Header(
date: date, date: date,
isInMonth: isInMonth, isInMonth: isInMonth,
isToday: isToday, isToday: isToday,
onCreate: () => onCreateEvent(date), onCreate: () => onCreateEvent(date),
), ),
VSpace(GridSize.typeOptionSeparatorHeight), ),
Flexible( VSpace(GridSize.typeOptionSeparatorHeight),
child: ListView.separated( Flexible(
itemBuilder: (BuildContext context, int index) { child: ListView.separated(
return children[index]; itemBuilder: (BuildContext context, int index) {
}, return children[index];
itemCount: children.length, },
separatorBuilder: (BuildContext context, int index) => itemCount: children.length,
VSpace(GridSize.typeOptionSeparatorHeight), padding: const EdgeInsets.symmetric(horizontal: 8.0),
), separatorBuilder: (BuildContext context, int index) =>
), VSpace(GridSize.typeOptionSeparatorHeight),
], ),
)); ),
],
);
return Container( return Container(
color: backgroundColor, color: backgroundColor,
@ -93,7 +95,10 @@ class CalendarDayCard extends StatelessWidget {
cursor: SystemMouseCursors.click, cursor: SystemMouseCursors.click,
onEnter: (p) => notifyEnter(context, true), onEnter: (p) => notifyEnter(context, true),
onExit: (p) => notifyEnter(context, false), onExit: (p) => notifyEnter(context, false),
child: child, child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: child,
),
), ),
); );
}), }),
@ -148,6 +153,15 @@ class _DayEventCell extends StatelessWidget {
onTap: onClick, onTap: onClick,
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8), padding: const EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(
border: Border.fromBorderSide(
BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0,
),
),
borderRadius: Corners.s6Border,
),
child: child, child: child,
), ),
), ),

View File

@ -73,9 +73,11 @@ class _CalendarPageState extends State<CalendarPage> {
listenWhen: (p, c) => p.updateEvent != c.updateEvent, listenWhen: (p, c) => p.updateEvent != c.updateEvent,
listener: (context, state) { listener: (context, state) {
if (state.updateEvent != null) { if (state.updateEvent != null) {
_eventController.removeWhere((element) => _eventController.removeWhere(
state.updateEvent!.event!.eventId == (element) =>
element.event!.eventId); state.updateEvent!.event!.eventId ==
element.event!.eventId,
);
_eventController.add(state.updateEvent!); _eventController.add(state.updateEvent!);
} }
}, },

View File

@ -18,6 +18,8 @@ import 'package:protobuf/protobuf.dart';
import 'calendar_setting.dart'; import 'calendar_setting.dart';
/// Widget that displays a list of settings that alters the appearance of the
/// calendar
class CalendarLayoutSetting extends StatefulWidget { class CalendarLayoutSetting extends StatefulWidget {
final CalendarSettingContext settingContext; final CalendarSettingContext settingContext;
final Function(CalendarLayoutSettingsPB? layoutSettings) onUpdated; final Function(CalendarLayoutSettingsPB? layoutSettings) onUpdated;
@ -96,18 +98,15 @@ class _CalendarLayoutSettingState extends State<CalendarLayoutSetting> {
fieldId: settings.layoutFieldId, fieldId: settings.layoutFieldId,
popoverMutex: popoverMutex, popoverMutex: popoverMutex,
onUpdated: (fieldId) { onUpdated: (fieldId) {
_updateLayoutSettings(context, _updateLayoutSettings(
onUpdated: widget.onUpdated, layoutFieldId: fieldId); context,
onUpdated: widget.onUpdated,
layoutFieldId: fieldId,
);
}, },
); );
default: default:
return ShowWeekends( return const SizedBox();
showWeekends: settings.showWeekends,
onUpdated: (showWeekends) {
_updateLayoutSettings(context,
onUpdated: widget.onUpdated, showWeekends: showWeekends);
},
);
} }
}).toList(); }).toList();
@ -129,7 +128,8 @@ class _CalendarLayoutSettingState extends State<CalendarLayoutSetting> {
} }
List<CalendarLayoutSettingAction> _availableCalendarSettings( List<CalendarLayoutSettingAction> _availableCalendarSettings(
CalendarLayoutSettingsPB layoutSettings) { CalendarLayoutSettingsPB layoutSettings,
) {
List<CalendarLayoutSettingAction> settings = [ List<CalendarLayoutSettingAction> settings = [
CalendarLayoutSettingAction.layoutField, CalendarLayoutSettingAction.layoutField,
// CalendarLayoutSettingAction.layoutType, // CalendarLayoutSettingAction.layoutType,
@ -220,8 +220,9 @@ class LayoutDateField extends StatelessWidget {
popupBuilder: (context) { popupBuilder: (context) {
return BlocProvider( return BlocProvider(
create: (context) => getIt<DatabasePropertyBloc>( create: (context) => getIt<DatabasePropertyBloc>(
param1: viewId, param2: fieldController) param1: viewId,
..add(const DatabasePropertyEvent.initial()), param2: fieldController,
)..add(const DatabasePropertyEvent.initial()),
child: BlocBuilder<DatabasePropertyBloc, DatabasePropertyState>( child: BlocBuilder<DatabasePropertyBloc, DatabasePropertyState>(
builder: (context, state) { builder: (context, state) {
final items = state.fieldContexts final items = state.fieldContexts
@ -264,7 +265,8 @@ class LayoutDateField extends StatelessWidget {
child: FlowyButton( child: FlowyButton(
margin: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 10.0), margin: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 10.0),
text: FlowyText.medium( text: FlowyText.medium(
LocaleKeys.calendar_settings_layoutDateField.tr()), LocaleKeys.calendar_settings_layoutDateField.tr(),
),
), ),
), ),
); );
@ -368,7 +370,8 @@ class FirstDayOfWeek extends StatelessWidget {
child: FlowyButton( child: FlowyButton(
margin: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 10.0), margin: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 10.0),
text: FlowyText.medium( text: FlowyText.medium(
LocaleKeys.calendar_settings_firstDayOfWeek.tr()), LocaleKeys.calendar_settings_firstDayOfWeek.tr(),
),
), ),
), ),
); );

View File

@ -15,8 +15,8 @@ import 'package:styled_widget/styled_widget.dart';
import 'calendar_layout_setting.dart'; import 'calendar_layout_setting.dart';
/// The highest-level widget shown in the popover triggered by clicking the /// The highest-level widget shown in the popover triggered by clicking the
/// "Settings" button. By default, shows [AllCalendarSettings] but upon /// "Settings" button. Shows [AllCalendarSettings] by default, but replaces its
/// selecting a category, replaces contents with contents of the submenu. /// contents with the submenu when a category is selected.
class CalendarSetting extends StatelessWidget { class CalendarSetting extends StatelessWidget {
final CalendarSettingContext settingContext; final CalendarSettingContext settingContext;
final CalendarLayoutSettingsPB? layoutSettings; final CalendarLayoutSettingsPB? layoutSettings;

View File

@ -45,10 +45,12 @@ class CheckboxFilterEditorBloc
didReceiveFilter: (FilterPB filter) { didReceiveFilter: (FilterPB filter) {
final filterInfo = state.filterInfo.copyWith(filter: filter); final filterInfo = state.filterInfo.copyWith(filter: filter);
final checkboxFilter = filterInfo.checkboxFilter()!; final checkboxFilter = filterInfo.checkboxFilter()!;
emit(state.copyWith( emit(
filterInfo: filterInfo, state.copyWith(
filter: checkboxFilter, filterInfo: filterInfo,
)); filter: checkboxFilter,
),
);
}, },
); );
}, },
@ -79,7 +81,8 @@ class CheckboxFilterEditorEvent with _$CheckboxFilterEditorEvent {
const factory CheckboxFilterEditorEvent.didReceiveFilter(FilterPB filter) = const factory CheckboxFilterEditorEvent.didReceiveFilter(FilterPB filter) =
_DidReceiveFilter; _DidReceiveFilter;
const factory CheckboxFilterEditorEvent.updateCondition( const factory CheckboxFilterEditorEvent.updateCondition(
CheckboxFilterConditionPB condition) = _UpdateCondition; CheckboxFilterConditionPB condition,
) = _UpdateCondition;
const factory CheckboxFilterEditorEvent.delete() = _Delete; const factory CheckboxFilterEditorEvent.delete() = _Delete;
} }

View File

@ -46,10 +46,12 @@ class ChecklistFilterEditorBloc
didReceiveFilter: (FilterPB filter) { didReceiveFilter: (FilterPB filter) {
final filterInfo = state.filterInfo.copyWith(filter: filter); final filterInfo = state.filterInfo.copyWith(filter: filter);
final checklistFilter = filterInfo.checklistFilter()!; final checklistFilter = filterInfo.checklistFilter()!;
emit(state.copyWith( emit(
filterInfo: filterInfo, state.copyWith(
filter: checklistFilter, filterInfo: filterInfo,
)); filter: checklistFilter,
),
);
}, },
); );
}, },
@ -82,7 +84,8 @@ class ChecklistFilterEditorEvent with _$ChecklistFilterEditorEvent {
const factory ChecklistFilterEditorEvent.didReceiveFilter(FilterPB filter) = const factory ChecklistFilterEditorEvent.didReceiveFilter(FilterPB filter) =
_DidReceiveFilter; _DidReceiveFilter;
const factory ChecklistFilterEditorEvent.updateCondition( const factory ChecklistFilterEditorEvent.updateCondition(
ChecklistFilterConditionPB condition) = _UpdateCondition; ChecklistFilterConditionPB condition,
) = _UpdateCondition;
const factory ChecklistFilterEditorEvent.delete() = _Delete; const factory ChecklistFilterEditorEvent.delete() = _Delete;
} }

View File

@ -14,11 +14,13 @@ class GridFilterMenuBloc
void Function(List<FieldInfo>)? _onFieldFn; void Function(List<FieldInfo>)? _onFieldFn;
GridFilterMenuBloc({required this.viewId, required this.fieldController}) GridFilterMenuBloc({required this.viewId, required this.fieldController})
: super(GridFilterMenuState.initial( : super(
viewId, GridFilterMenuState.initial(
fieldController.filterInfos, viewId,
fieldController.fieldInfos, fieldController.filterInfos,
)) { fieldController.fieldInfos,
),
) {
on<GridFilterMenuEvent>( on<GridFilterMenuEvent>(
(event, emit) async { (event, emit) async {
event.when( event.when(
@ -82,7 +84,8 @@ class GridFilterMenuBloc
class GridFilterMenuEvent with _$GridFilterMenuEvent { class GridFilterMenuEvent with _$GridFilterMenuEvent {
const factory GridFilterMenuEvent.initial() = _Initial; const factory GridFilterMenuEvent.initial() = _Initial;
const factory GridFilterMenuEvent.didReceiveFilters( const factory GridFilterMenuEvent.didReceiveFilters(
List<FilterInfo> filters) = _DidReceiveFilters; List<FilterInfo> filters,
) = _DidReceiveFilters;
const factory GridFilterMenuEvent.didReceiveFields(List<FieldInfo> fields) = const factory GridFilterMenuEvent.didReceiveFields(List<FieldInfo> fields) =
_DidReceiveFields; _DidReceiveFields;
const factory GridFilterMenuEvent.toggleMenu() = _SetMenuVisibility; const factory GridFilterMenuEvent.toggleMenu() = _SetMenuVisibility;

View File

@ -61,10 +61,12 @@ class SelectOptionFilterEditorBloc
didReceiveFilter: (FilterPB filter) { didReceiveFilter: (FilterPB filter) {
final filterInfo = state.filterInfo.copyWith(filter: filter); final filterInfo = state.filterInfo.copyWith(filter: filter);
final selectOptionFilter = filterInfo.selectOptionFilter()!; final selectOptionFilter = filterInfo.selectOptionFilter()!;
emit(state.copyWith( emit(
filterInfo: filterInfo, state.copyWith(
filter: selectOptionFilter, filterInfo: filterInfo,
)); filter: selectOptionFilter,
),
);
}, },
updateFilterDescription: (String desc) { updateFilterDescription: (String desc) {
emit(state.copyWith(filterDesc: desc)); emit(state.copyWith(filterDesc: desc));
@ -112,13 +114,17 @@ class SelectOptionFilterEditorBloc
class SelectOptionFilterEditorEvent with _$SelectOptionFilterEditorEvent { class SelectOptionFilterEditorEvent with _$SelectOptionFilterEditorEvent {
const factory SelectOptionFilterEditorEvent.initial() = _Initial; const factory SelectOptionFilterEditorEvent.initial() = _Initial;
const factory SelectOptionFilterEditorEvent.didReceiveFilter( const factory SelectOptionFilterEditorEvent.didReceiveFilter(
FilterPB filter) = _DidReceiveFilter; FilterPB filter,
) = _DidReceiveFilter;
const factory SelectOptionFilterEditorEvent.updateCondition( const factory SelectOptionFilterEditorEvent.updateCondition(
SelectOptionConditionPB condition) = _UpdateCondition; SelectOptionConditionPB condition,
) = _UpdateCondition;
const factory SelectOptionFilterEditorEvent.updateContent( const factory SelectOptionFilterEditorEvent.updateContent(
List<String> optionIds) = _UpdateContent; List<String> optionIds,
) = _UpdateContent;
const factory SelectOptionFilterEditorEvent.updateFilterDescription( const factory SelectOptionFilterEditorEvent.updateFilterDescription(
String desc) = _UpdateDesc; String desc,
) = _UpdateDesc;
const factory SelectOptionFilterEditorEvent.delete() = _Delete; const factory SelectOptionFilterEditorEvent.delete() = _Delete;
} }

View File

@ -43,15 +43,22 @@ class SelectOptionFilterListBloc<T>
didReceiveOptions: (newOptions) { didReceiveOptions: (newOptions) {
List<SelectOptionPB> options = List.from(newOptions); List<SelectOptionPB> options = List.from(newOptions);
options.retainWhere( options.retainWhere(
(element) => element.name.contains(state.predicate)); (element) => element.name.contains(state.predicate),
);
final visibleOptions = options.map((option) { final visibleOptions = options.map((option) {
return VisibleSelectOption( return VisibleSelectOption(
option, state.selectedOptionIds.contains(option.id)); option,
state.selectedOptionIds.contains(option.id),
);
}).toList(); }).toList();
emit(state.copyWith( emit(
options: options, visibleOptions: visibleOptions)); state.copyWith(
options: options,
visibleOptions: visibleOptions,
),
);
}, },
filterOption: (optionName) { filterOption: (optionName) {
_updateSelectOptions(predicate: optionName, emit: emit); _updateSelectOptions(predicate: optionName, emit: emit);
@ -71,11 +78,13 @@ class SelectOptionFilterListBloc<T>
selectedOptionIds ?? state.selectedOptionIds, selectedOptionIds ?? state.selectedOptionIds,
); );
emit(state.copyWith( emit(
predicate: predicate ?? state.predicate, state.copyWith(
visibleOptions: visibleOptions, predicate: predicate ?? state.predicate,
selectedOptionIds: selectedOptionIds ?? state.selectedOptionIds, visibleOptions: visibleOptions,
)); selectedOptionIds: selectedOptionIds ?? state.selectedOptionIds,
),
);
} }
List<VisibleSelectOption> _makeVisibleOptions( List<VisibleSelectOption> _makeVisibleOptions(
@ -105,11 +114,14 @@ class SelectOptionFilterListBloc<T>
class SelectOptionFilterListEvent with _$SelectOptionFilterListEvent { class SelectOptionFilterListEvent with _$SelectOptionFilterListEvent {
const factory SelectOptionFilterListEvent.initial() = _Initial; const factory SelectOptionFilterListEvent.initial() = _Initial;
const factory SelectOptionFilterListEvent.selectOption( const factory SelectOptionFilterListEvent.selectOption(
SelectOptionPB option) = _SelectOption; SelectOptionPB option,
) = _SelectOption;
const factory SelectOptionFilterListEvent.unselectOption( const factory SelectOptionFilterListEvent.unselectOption(
SelectOptionPB option) = _UnSelectOption; SelectOptionPB option,
) = _UnSelectOption;
const factory SelectOptionFilterListEvent.didReceiveOptions( const factory SelectOptionFilterListEvent.didReceiveOptions(
List<SelectOptionPB> options) = _DidReceiveOptions; List<SelectOptionPB> options,
) = _DidReceiveOptions;
const factory SelectOptionFilterListEvent.filterOption(String optionName) = const factory SelectOptionFilterListEvent.filterOption(String optionName) =
_SelectOptionFilter; _SelectOptionFilter;
} }

View File

@ -54,10 +54,12 @@ class TextFilterEditorBloc
didReceiveFilter: (FilterPB filter) { didReceiveFilter: (FilterPB filter) {
final filterInfo = state.filterInfo.copyWith(filter: filter); final filterInfo = state.filterInfo.copyWith(filter: filter);
final textFilter = filterInfo.textFilter()!; final textFilter = filterInfo.textFilter()!;
emit(state.copyWith( emit(
filterInfo: filterInfo, state.copyWith(
filter: textFilter, filterInfo: filterInfo,
)); filter: textFilter,
),
);
}, },
); );
}, },
@ -88,7 +90,8 @@ class TextFilterEditorEvent with _$TextFilterEditorEvent {
const factory TextFilterEditorEvent.didReceiveFilter(FilterPB filter) = const factory TextFilterEditorEvent.didReceiveFilter(FilterPB filter) =
_DidReceiveFilter; _DidReceiveFilter;
const factory TextFilterEditorEvent.updateCondition( const factory TextFilterEditorEvent.updateCondition(
TextFilterConditionPB condition) = _UpdateCondition; TextFilterConditionPB condition,
) = _UpdateCondition;
const factory TextFilterEditorEvent.updateContent(String content) = const factory TextFilterEditorEvent.updateContent(String content) =
_UpdateContent; _UpdateContent;
const factory TextFilterEditorEvent.delete() = _Delete; const factory TextFilterEditorEvent.delete() = _Delete;

View File

@ -8,9 +8,11 @@ class GridAccessoryMenuBloc
final String viewId; final String viewId;
GridAccessoryMenuBloc({required this.viewId}) GridAccessoryMenuBloc({required this.viewId})
: super(GridAccessoryMenuState.initial( : super(
viewId, GridAccessoryMenuState.initial(
)) { viewId,
),
) {
on<GridAccessoryMenuEvent>( on<GridAccessoryMenuEvent>(
(event, emit) async { (event, emit) async {
event.when( event.when(

View File

@ -39,16 +39,20 @@ class GridBloc extends Bloc<GridEvent, GridState> {
emit(state.copyWith(grid: Some(grid))); emit(state.copyWith(grid: Some(grid)));
}, },
didReceiveFieldUpdate: (fields) { didReceiveFieldUpdate: (fields) {
emit(state.copyWith( emit(
fields: GridFieldEquatable(fields), state.copyWith(
)); fields: GridFieldEquatable(fields),
),
);
}, },
didReceiveRowUpdate: (newRowInfos, reason) { didReceiveRowUpdate: (newRowInfos, reason) {
emit(state.copyWith( emit(
rowInfos: newRowInfos, state.copyWith(
rowCount: newRowInfos.length, rowInfos: newRowInfos,
reason: reason, rowCount: newRowInfos.length,
)); reason: reason,
),
);
}, },
); );
}, },
@ -146,7 +150,8 @@ class GridState with _$GridState {
class GridLoadingState with _$GridLoadingState { class GridLoadingState with _$GridLoadingState {
const factory GridLoadingState.loading() = _Loading; const factory GridLoadingState.loading() = _Loading;
const factory GridLoadingState.finish( const factory GridLoadingState.finish(
Either<Unit, FlowyError> successOrFail) = _Finish; Either<Unit, FlowyError> successOrFail,
) = _Finish;
} }
class GridFieldEquatable extends Equatable { class GridFieldEquatable extends Equatable {

View File

@ -40,7 +40,9 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
} }
Future<void> _moveField( Future<void> _moveField(
_MoveField value, Emitter<GridHeaderState> emit) async { _MoveField value,
Emitter<GridHeaderState> emit,
) async {
final fields = List<FieldInfo>.from(state.fields); final fields = List<FieldInfo>.from(state.fields);
fields.insert(value.toIndex, fields.removeAt(value.fromIndex)); fields.insert(value.toIndex, fields.removeAt(value.fromIndex));
emit(state.copyWith(fields: fields)); emit(state.copyWith(fields: fields));
@ -69,7 +71,10 @@ class GridHeaderEvent with _$GridHeaderEvent {
const factory GridHeaderEvent.didReceiveFieldUpdate(List<FieldInfo> fields) = const factory GridHeaderEvent.didReceiveFieldUpdate(List<FieldInfo> fields) =
_DidReceiveFieldUpdate; _DidReceiveFieldUpdate;
const factory GridHeaderEvent.moveField( const factory GridHeaderEvent.moveField(
FieldPB field, int fromIndex, int toIndex) = _MoveField; FieldPB field,
int fromIndex,
int toIndex,
) = _MoveField;
} }
@freezed @freezed

View File

@ -35,11 +35,13 @@ class RowBloc extends Bloc<RowEvent, RowState> {
final cells = cellByFieldId.values final cells = cellByFieldId.values
.map((e) => GridCellEquatable(e.fieldInfo)) .map((e) => GridCellEquatable(e.fieldInfo))
.toList(); .toList();
emit(state.copyWith( emit(
cellByFieldId: cellByFieldId, state.copyWith(
cells: UnmodifiableListView(cells), cellByFieldId: cellByFieldId,
changeReason: reason, cells: UnmodifiableListView(cells),
)); changeReason: reason,
),
);
}, },
); );
}, },
@ -68,8 +70,9 @@ class RowEvent with _$RowEvent {
const factory RowEvent.initial() = _InitialRow; const factory RowEvent.initial() = _InitialRow;
const factory RowEvent.createRow() = _CreateRow; const factory RowEvent.createRow() = _CreateRow;
const factory RowEvent.didReceiveCells( const factory RowEvent.didReceiveCells(
CellByFieldId cellsByFieldId, RowsChangedReason reason) = CellByFieldId cellsByFieldId,
_DidReceiveCells; RowsChangedReason reason,
) = _DidReceiveCells;
} }
@freezed @freezed

View File

@ -59,7 +59,8 @@ class RowDetailEvent with _$RowDetailEvent {
const factory RowDetailEvent.initial() = _Initial; const factory RowDetailEvent.initial() = _Initial;
const factory RowDetailEvent.deleteField(String fieldId) = _DeleteField; const factory RowDetailEvent.deleteField(String fieldId) = _DeleteField;
const factory RowDetailEvent.didReceiveCellDatas( const factory RowDetailEvent.didReceiveCellDatas(
List<CellIdentifier> gridCells) = _DidReceiveCellDatas; List<CellIdentifier> gridCells,
) = _DidReceiveCellDatas;
} }
@freezed @freezed

View File

@ -82,9 +82,10 @@ class CreateSortBloc extends Bloc<CreateSortEvent, CreateSortState> {
Future<Either<Unit, FlowyError>> _createDefaultSort(FieldInfo field) async { Future<Either<Unit, FlowyError>> _createDefaultSort(FieldInfo field) async {
final result = await _sortBackendSvc.insertSort( final result = await _sortBackendSvc.insertSort(
fieldId: field.id, fieldId: field.id,
fieldType: field.fieldType, fieldType: field.fieldType,
condition: SortConditionPB.Ascending); condition: SortConditionPB.Ascending,
);
return result; return result;
} }

View File

@ -100,7 +100,9 @@ class SortEditorEvent with _$SortEditorEvent {
const factory SortEditorEvent.didReceiveSorts(List<SortInfo> sortInfos) = const factory SortEditorEvent.didReceiveSorts(List<SortInfo> sortInfos) =
_DidReceiveSorts; _DidReceiveSorts;
const factory SortEditorEvent.setCondition( const factory SortEditorEvent.setCondition(
SortInfo sortInfo, SortConditionPB condition) = _SetCondition; SortInfo sortInfo,
SortConditionPB condition,
) = _SetCondition;
const factory SortEditorEvent.deleteSort(SortInfo sortInfo) = _DeleteSort; const factory SortEditorEvent.deleteSort(SortInfo sortInfo) = _DeleteSort;
const factory SortEditorEvent.deleteAllSorts() = _DeleteAllSorts; const factory SortEditorEvent.deleteAllSorts() = _DeleteAllSorts;
} }

View File

@ -14,11 +14,13 @@ class SortMenuBloc extends Bloc<SortMenuEvent, SortMenuState> {
void Function(List<FieldInfo>)? _onFieldFn; void Function(List<FieldInfo>)? _onFieldFn;
SortMenuBloc({required this.viewId, required this.fieldController}) SortMenuBloc({required this.viewId, required this.fieldController})
: super(SortMenuState.initial( : super(
viewId, SortMenuState.initial(
fieldController.sortInfos, viewId,
fieldController.fieldInfos, fieldController.sortInfos,
)) { fieldController.fieldInfos,
),
) {
on<SortMenuEvent>( on<SortMenuEvent>(
(event, emit) async { (event, emit) async {
event.when( event.when(

View File

@ -117,7 +117,8 @@ class FlowyGrid extends StatefulWidget {
class _FlowyGridState extends State<FlowyGrid> { class _FlowyGridState extends State<FlowyGrid> {
final _scrollController = GridScrollController( final _scrollController = GridScrollController(
scrollGroupController: LinkedScrollControllerGroup()); scrollGroupController: LinkedScrollControllerGroup(),
);
late ScrollController headerScrollController; late ScrollController headerScrollController;
@override @override
@ -319,13 +320,14 @@ class _GridRowsState extends State<_GridRows> {
); );
FlowyOverlay.show( FlowyOverlay.show(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return RowDetailPage( return RowDetailPage(
cellBuilder: cellBuilder, cellBuilder: cellBuilder,
dataController: dataController, dataController: dataController,
); );
}); },
);
} }
} }

View File

@ -8,9 +8,9 @@ class GridScrollController {
final List<ScrollController> _linkHorizontalControllers = []; final List<ScrollController> _linkHorizontalControllers = [];
GridScrollController( GridScrollController({
{required LinkedScrollControllerGroup scrollGroupController}) required LinkedScrollControllerGroup scrollGroupController,
: _scrollGroupController = scrollGroupController, }) : _scrollGroupController = scrollGroupController,
verticalController = ScrollController(), verticalController = ScrollController(),
horizontalController = scrollGroupController.addAndGet(); horizontalController = scrollGroupController.addAndGet();

View File

@ -28,10 +28,7 @@ class GridSize {
vertical: GridSize.cellVPadding, vertical: GridSize.cellVPadding,
); );
static EdgeInsets get typeOptionContentInsets => const EdgeInsets.symmetric( static EdgeInsets get typeOptionContentInsets => const EdgeInsets.all(4);
horizontal: 6,
vertical: 2,
);
static EdgeInsets get footerContentInsets => EdgeInsets.fromLTRB( static EdgeInsets get footerContentInsets => EdgeInsets.fromLTRB(
GridSize.leadingHeaderPadding, GridSize.leadingHeaderPadding,

View File

@ -21,10 +21,11 @@ class GridAccessoryMenu extends StatelessWidget {
child: MultiBlocListener( child: MultiBlocListener(
listeners: [ listeners: [
BlocListener<GridFilterMenuBloc, GridFilterMenuState>( BlocListener<GridFilterMenuBloc, GridFilterMenuState>(
listenWhen: (p, c) => p.isVisible != c.isVisible, listenWhen: (p, c) => p.isVisible != c.isVisible,
listener: (context, state) => context listener: (context, state) => context
.read<GridAccessoryMenuBloc>() .read<GridAccessoryMenuBloc>()
.add(const GridAccessoryMenuEvent.toggleMenu())), .add(const GridAccessoryMenuEvent.toggleMenu()),
),
BlocListener<SortMenuBloc, SortMenuState>( BlocListener<SortMenuBloc, SortMenuState>(
listenWhen: (p, c) => p.isVisible != c.isVisible, listenWhen: (p, c) => p.isVisible != c.isVisible,
listener: (context, state) => context listener: (context, state) => context

View File

@ -100,7 +100,9 @@ class _CheckboxFilterEditorState extends State<CheckboxFilterEditor> {
} }
Widget _buildFilterPanel( Widget _buildFilterPanel(
BuildContext context, CheckboxFilterEditorState state) { BuildContext context,
CheckboxFilterEditorState state,
) {
return SizedBox( return SizedBox(
height: 20, height: 20,
child: Row( child: Row(

View File

@ -70,9 +70,11 @@ class _ChecklistFilterChoicechipState extends State<ChecklistFilterChoicechip> {
class ChecklistFilterEditor extends StatefulWidget { class ChecklistFilterEditor extends StatefulWidget {
final ChecklistFilterEditorBloc bloc; final ChecklistFilterEditorBloc bloc;
final PopoverMutex popoverMutex; final PopoverMutex popoverMutex;
const ChecklistFilterEditor( const ChecklistFilterEditor({
{required this.bloc, required this.popoverMutex, Key? key}) required this.bloc,
: super(key: key); required this.popoverMutex,
Key? key,
}) : super(key: key);
@override @override
ChecklistState createState() => ChecklistState(); ChecklistState createState() => ChecklistState();

View File

@ -65,8 +65,10 @@ class _ChoicechipFilterDesc extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final arrow = Transform.rotate( final arrow = Transform.rotate(
angle: -math.pi / 2, angle: -math.pi / 2,
child: svgWidget("home/arrow_left", child: svgWidget(
color: AFThemeExtension.of(context).textColor), "home/arrow_left",
color: AFThemeExtension.of(context).textColor,
),
); );
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 2), padding: const EdgeInsets.symmetric(horizontal: 2),

View File

@ -110,7 +110,10 @@ class _SelectOptionFilterEditorState extends State<SelectOptionFilterEditor> {
selectedOptionIds: state.filter.optionIds, selectedOptionIds: state.filter.optionIds,
onSelectedOptions: (optionIds) { onSelectedOptions: (optionIds) {
context.read<SelectOptionFilterEditorBloc>().add( context.read<SelectOptionFilterEditorBloc>().add(
SelectOptionFilterEditorEvent.updateContent(optionIds)); SelectOptionFilterEditorEvent.updateContent(
optionIds,
),
);
}, },
), ),
), ),
@ -132,7 +135,9 @@ class _SelectOptionFilterEditorState extends State<SelectOptionFilterEditor> {
} }
Widget _buildFilterPanel( Widget _buildFilterPanel(
BuildContext context, SelectOptionFilterEditorState state) { BuildContext context,
SelectOptionFilterEditorState state,
) {
return SizedBox( return SizedBox(
height: 20, height: 20,
child: Row( child: Row(
@ -144,7 +149,8 @@ class _SelectOptionFilterEditorState extends State<SelectOptionFilterEditor> {
popoverMutex: popoverMutex, popoverMutex: popoverMutex,
onCondition: (condition) { onCondition: (condition) {
context.read<SelectOptionFilterEditorBloc>().add( context.read<SelectOptionFilterEditorBloc>().add(
SelectOptionFilterEditorEvent.updateCondition(condition)); SelectOptionFilterEditorEvent.updateCondition(condition),
);
}, },
), ),
const Spacer(), const Spacer(),

View File

@ -147,7 +147,9 @@ class _TextFilterEditorState extends State<TextFilterEditor> {
} }
Widget _buildFilterTextField( Widget _buildFilterTextField(
BuildContext context, TextFilterEditorState state) { BuildContext context,
TextFilterEditorState state,
) {
return FlowyTextField( return FlowyTextField(
text: state.filter.content, text: state.filter.content,
hintText: LocaleKeys.grid_settings_typeAValue.tr(), hintText: LocaleKeys.grid_settings_typeAValue.tr(),

View File

@ -18,8 +18,10 @@ class ConditionButton extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final arrow = Transform.rotate( final arrow = Transform.rotate(
angle: -math.pi / 2, angle: -math.pi / 2,
child: svgWidget("home/arrow_left", child: svgWidget(
color: AFThemeExtension.of(context).textColor), "home/arrow_left",
color: AFThemeExtension.of(context).textColor,
),
); );
return SizedBox( return SizedBox(

View File

@ -117,7 +117,10 @@ class _FilterTextFieldDelegate extends SliverPersistentHeaderDelegate {
@override @override
Widget build( Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) { BuildContext context,
double shrinkOffset,
bool overlapsContent,
) {
return Container( return Container(
padding: const EdgeInsets.only(top: 4), padding: const EdgeInsets.only(top: 4),
height: fixHeight, height: fixHeight,

View File

@ -95,17 +95,20 @@ class _GridHeaderCellContainer extends StatelessWidget {
width: 1.0, width: 1.0,
); );
final decoration = BoxDecoration( final decoration = BoxDecoration(
border: Border( border: Border(
top: borderSide, top: borderSide,
right: borderSide, right: borderSide,
bottom: borderSide, bottom: borderSide,
)); ),
);
return Container( return Container(
width: width, width: width,
decoration: decoration, decoration: decoration,
child: ConstrainedBox( child: ConstrainedBox(
constraints: const BoxConstraints.expand(), child: child), constraints: const BoxConstraints.expand(),
child: child,
),
); );
} }
} }
@ -149,10 +152,12 @@ class FieldCellButton extends StatelessWidget {
final VoidCallback onTap; final VoidCallback onTap;
final FieldPB field; final FieldPB field;
final int? maxLines; final int? maxLines;
final BorderRadius? radius;
const FieldCellButton({ const FieldCellButton({
required this.field, required this.field,
required this.onTap, required this.onTap,
this.maxLines = 1, this.maxLines = 1,
this.radius = BorderRadius.zero,
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
@ -169,7 +174,7 @@ class FieldCellButton extends StatelessWidget {
leftIcon: FlowySvg( leftIcon: FlowySvg(
name: field.fieldType.iconName(), name: field.fieldType.iconName(),
), ),
radius: BorderRadius.zero, radius: radius,
text: FlowyText.medium( text: FlowyText.medium(
text, text,
maxLines: maxLines, maxLines: maxLines,

View File

@ -101,25 +101,27 @@ class _FieldOperationList extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column(children: [ return Column(
Flex( children: [
direction: Axis.horizontal, Flex(
children: [ direction: Axis.horizontal,
_actionCell(FieldAction.hide), children: [
HSpace(GridSize.typeOptionSeparatorHeight), _actionCell(FieldAction.hide),
_actionCell(FieldAction.duplicate), HSpace(GridSize.typeOptionSeparatorHeight),
], _actionCell(FieldAction.duplicate),
), ],
VSpace(GridSize.typeOptionSeparatorHeight), ),
Flex( VSpace(GridSize.typeOptionSeparatorHeight),
direction: Axis.horizontal, Flex(
children: [ direction: Axis.horizontal,
_actionCell(FieldAction.delete), children: [
HSpace(GridSize.typeOptionSeparatorHeight), _actionCell(FieldAction.delete),
const Spacer(), HSpace(GridSize.typeOptionSeparatorHeight),
], const Spacer(),
), ],
]); ),
],
);
} }
Widget _actionCell(FieldAction action) { Widget _actionCell(FieldAction action) {

View File

@ -90,11 +90,13 @@ class _SwitchFieldButton extends StatelessWidget {
mutex: popoverMutex, mutex: popoverMutex,
offset: const Offset(8, 0), offset: const Offset(8, 0),
popupBuilder: (popOverContext) { popupBuilder: (popOverContext) {
return FieldTypeList(onSelectField: (newFieldType) { return FieldTypeList(
context onSelectField: (newFieldType) {
.read<FieldTypeOptionEditBloc>() context
.add(FieldTypeOptionEditEvent.switchToField(newFieldType)); .read<FieldTypeOptionEditBloc>()
}); .add(FieldTypeOptionEditEvent.switchToField(newFieldType));
},
);
}, },
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0), padding: const EdgeInsets.symmetric(horizontal: 12.0),

View File

@ -39,7 +39,9 @@ class _GridHeaderSliverAdaptorState extends State<GridHeaderSliverAdaptor> {
return BlocProvider( return BlocProvider(
create: (context) { create: (context) {
final bloc = getIt<GridHeaderBloc>( final bloc = getIt<GridHeaderBloc>(
param1: widget.viewId, param2: widget.fieldController); param1: widget.viewId,
param2: widget.fieldController,
);
bloc.add(const GridHeaderEvent.initial()); bloc.add(const GridHeaderEvent.initial());
return bloc; return bloc;
}, },
@ -98,10 +100,16 @@ class _GridHeaderState extends State<_GridHeader> {
builder: (context, state) { builder: (context, state) {
final cells = state.fields final cells = state.fields
.where((field) => field.visibility) .where((field) => field.visibility)
.map((field) => .map(
FieldCellContext(viewId: widget.viewId, field: field.field)) (field) =>
.map((ctx) => FieldCellContext(viewId: widget.viewId, field: field.field),
GridFieldCell(key: _getKeyById(ctx.field.id), cellContext: ctx)) )
.map(
(ctx) => GridFieldCell(
key: _getKeyById(ctx.field.id),
cellContext: ctx,
),
)
.toList(); .toList();
return Container( return Container(
@ -124,8 +132,12 @@ class _GridHeaderState extends State<_GridHeader> {
); );
} }
void _onReorder(List<GridFieldCell> cells, int oldIndex, BuildContext context, void _onReorder(
int newIndex) { List<GridFieldCell> cells,
int oldIndex,
BuildContext context,
int newIndex,
) {
if (cells.length > oldIndex) { if (cells.length > oldIndex) {
final field = cells[oldIndex].cellContext.field; final field = cells[oldIndex].cellContext.field;
context context
@ -177,7 +189,7 @@ class CreateFieldButton extends StatelessWidget {
constraints: BoxConstraints.loose(const Size(240, 600)), constraints: BoxConstraints.loose(const Size(240, 600)),
child: FlowyButton( child: FlowyButton(
radius: BorderRadius.zero, radius: BorderRadius.zero,
text: FlowyText.medium(LocaleKeys.grid_field_newColumn.tr()), text: FlowyText.medium(LocaleKeys.grid_field_newProperty.tr()),
hoverColor: AFThemeExtension.of(context).greyHover, hoverColor: AFThemeExtension.of(context).greyHover,
onTap: () {}, onTap: () {},
leftIcon: const FlowySvg(name: 'home/add'), leftIcon: const FlowySvg(name: 'home/add'),
@ -197,12 +209,17 @@ class SliverHeaderDelegateImplementation
final String gridId; final String gridId;
final List<FieldPB> fields; final List<FieldPB> fields;
SliverHeaderDelegateImplementation( SliverHeaderDelegateImplementation({
{required this.gridId, required this.fields}); required this.gridId,
required this.fields,
});
@override @override
Widget build( Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) { BuildContext context,
double shrinkOffset,
bool overlapsContent,
) {
return _GridHeader(viewId: gridId); return _GridHeader(viewId: gridId);
} }

View File

@ -76,12 +76,13 @@ TypeOptionWidgetBuilder makeTypeOptionWidgetBuilder({
); );
case FieldType.DateTime: case FieldType.DateTime:
return DateTypeOptionWidgetBuilder( return DateTypeOptionWidgetBuilder(
makeTypeOptionContextWithDataController<DateTypeOptionPB>( makeTypeOptionContextWithDataController<DateTypeOptionPB>(
viewId: viewId, viewId: viewId,
fieldType: fieldType, fieldType: fieldType,
dataController: dataController, dataController: dataController,
), ),
popoverMutex); popoverMutex,
);
case FieldType.SingleSelect: case FieldType.SingleSelect:
return SingleSelectTypeOptionWidgetBuilder( return SingleSelectTypeOptionWidgetBuilder(
makeTypeOptionContextWithDataController<SingleSelectTypeOptionPB>( makeTypeOptionContextWithDataController<SingleSelectTypeOptionPB>(

View File

@ -4,7 +4,8 @@ import 'builder.dart';
class ChecklistTypeOptionWidgetBuilder extends TypeOptionWidgetBuilder { class ChecklistTypeOptionWidgetBuilder extends TypeOptionWidgetBuilder {
ChecklistTypeOptionWidgetBuilder( ChecklistTypeOptionWidgetBuilder(
ChecklistTypeOptionContext typeOptionContext); ChecklistTypeOptionContext typeOptionContext,
);
@override @override
Widget? build(BuildContext context) => null; Widget? build(BuildContext context) => null;

View File

@ -299,6 +299,8 @@ extension DateFormatExtension on DateFormat {
return LocaleKeys.grid_field_dateFormatLocal.tr(); return LocaleKeys.grid_field_dateFormatLocal.tr();
case DateFormat.US: case DateFormat.US:
return LocaleKeys.grid_field_dateFormatUS.tr(); return LocaleKeys.grid_field_dateFormatUS.tr();
case DateFormat.DayMonthYear:
return LocaleKeys.grid_field_dateFormatDayMonthYear.tr();
default: default:
throw UnimplementedError; throw UnimplementedError;
} }

View File

@ -3,6 +3,7 @@ import 'package:appflowy/plugins/database_view/application/field/type_option/num
import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_context.dart'; import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_context.dart';
import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:appflowy_backend/protobuf/flowy-database/format.pbenum.dart'; import 'package:appflowy_backend/protobuf/flowy-database/format.pbenum.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -28,10 +29,13 @@ class NumberTypeOptionWidgetBuilder extends TypeOptionWidgetBuilder {
@override @override
Widget? build(BuildContext context) { Widget? build(BuildContext context) {
return Column(children: [ return Column(
VSpace(GridSize.typeOptionSeparatorHeight), children: [
_widget, VSpace(GridSize.typeOptionSeparatorHeight),
]); const TypeOptionSeparator(),
_widget,
],
);
} }
} }
@ -49,55 +53,65 @@ class NumberTypeOptionWidget extends TypeOptionWidget {
return BlocProvider( return BlocProvider(
create: (context) => create: (context) =>
NumberTypeOptionBloc(typeOptionContext: typeOptionContext), NumberTypeOptionBloc(typeOptionContext: typeOptionContext),
child: SizedBox( child: BlocConsumer<NumberTypeOptionBloc, NumberTypeOptionState>(
height: GridSize.popoverItemHeight, listener: (context, state) =>
child: BlocConsumer<NumberTypeOptionBloc, NumberTypeOptionState>( typeOptionContext.typeOption = state.typeOption,
listener: (context, state) => builder: (context, state) {
typeOptionContext.typeOption = state.typeOption, final selectNumUnitButton = SizedBox(
builder: (context, state) { height: GridSize.popoverItemHeight,
final button = SizedBox( child: FlowyButton(
height: GridSize.popoverItemHeight, hoverColor: AFThemeExtension.of(context).lightGreyHover,
child: FlowyButton( margin: GridSize.typeOptionContentInsets,
margin: GridSize.typeOptionContentInsets, rightIcon: svgWidget(
rightIcon: svgWidget( "grid/more",
"grid/more", color: AFThemeExtension.of(context).textColor,
color: Theme.of(context).iconTheme.color,
),
text: Row(
children: [
FlowyText.medium(LocaleKeys.grid_field_numberFormat.tr()),
const Spacer(),
FlowyText.regular(state.typeOption.format.title()),
],
),
), ),
); text: FlowyText.regular(
state.typeOption.format.title(),
color: AFThemeExtension.of(context).textColor,
),
),
);
return AppFlowyPopover( final numFormatTitle = Container(
mutex: popoverMutex, padding: const EdgeInsets.only(left: 6),
triggerActions: height: GridSize.popoverItemHeight,
PopoverTriggerFlags.hover | PopoverTriggerFlags.click, alignment: Alignment.centerLeft,
offset: const Offset(8, 0), child: FlowyText.medium(
constraints: BoxConstraints.loose(const Size(460, 440)), LocaleKeys.grid_field_numberFormat.tr(),
margin: EdgeInsets.zero, color: AFThemeExtension.of(context).textColor,
child: Padding( ),
padding: const EdgeInsets.symmetric(horizontal: 12.0), );
child: button, return Padding(
), padding: const EdgeInsets.symmetric(horizontal: 12),
popupBuilder: (BuildContext popoverContext) { child: Column(
return NumberFormatList( crossAxisAlignment: CrossAxisAlignment.start,
onSelected: (format) { children: [
context numFormatTitle,
.read<NumberTypeOptionBloc>() AppFlowyPopover(
.add(NumberTypeOptionEvent.didSelectFormat(format)); mutex: popoverMutex,
PopoverContainer.of(popoverContext).close(); triggerActions:
PopoverTriggerFlags.hover | PopoverTriggerFlags.click,
offset: const Offset(8, 0),
constraints: BoxConstraints.loose(const Size(460, 440)),
margin: EdgeInsets.zero,
child: selectNumUnitButton,
popupBuilder: (BuildContext popoverContext) {
return NumberFormatList(
onSelected: (format) {
context
.read<NumberTypeOptionBloc>()
.add(NumberTypeOptionEvent.didSelectFormat(format));
PopoverContainer.of(popoverContext).close();
},
selectedFormat: state.typeOption.format,
);
}, },
selectedFormat: state.typeOption.format, ),
); ],
}, ),
); );
}, },
),
), ),
); );
} }
@ -108,9 +122,11 @@ typedef SelectNumberFormatCallback = Function(NumberFormat format);
class NumberFormatList extends StatelessWidget { class NumberFormatList extends StatelessWidget {
final SelectNumberFormatCallback onSelected; final SelectNumberFormatCallback onSelected;
final NumberFormat selectedFormat; final NumberFormat selectedFormat;
const NumberFormatList( const NumberFormatList({
{required this.selectedFormat, required this.onSelected, Key? key}) required this.selectedFormat,
: super(key: key); required this.onSelected,
Key? key,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -127,11 +143,12 @@ class NumberFormatList extends StatelessWidget {
builder: (context, state) { builder: (context, state) {
final cells = state.formats.map((format) { final cells = state.formats.map((format) {
return NumberFormatCell( return NumberFormatCell(
isSelected: format == selectedFormat, isSelected: format == selectedFormat,
format: format, format: format,
onSelected: (format) { onSelected: (format) {
onSelected(format); onSelected(format);
}); },
);
}).toList(); }).toList();
final list = ListView.separated( final list = ListView.separated(

View File

@ -71,9 +71,11 @@ class OptionTitle extends StatelessWidget {
return BlocBuilder<SelectOptionTypeOptionBloc, SelectOptionTypeOptionState>( return BlocBuilder<SelectOptionTypeOptionBloc, SelectOptionTypeOptionState>(
builder: (context, state) { builder: (context, state) {
List<Widget> children = [ List<Widget> children = [
FlowyText.medium( Padding(
LocaleKeys.grid_field_optionTitle.tr(), padding: const EdgeInsets.only(left: 9),
color: Theme.of(context).hintColor, child: FlowyText.medium(
LocaleKeys.grid_field_optionTitle.tr(),
),
) )
]; ];
if (state.options.isNotEmpty && !state.isEditingOption) { if (state.options.isNotEmpty && !state.isEditingOption) {

View File

@ -66,7 +66,8 @@ class SelectOptionTypeOptionEditor extends StatelessWidget {
if (showOptions) { if (showOptions) {
cells.add(const TypeOptionSeparator()); cells.add(const TypeOptionSeparator());
cells.add( cells.add(
SelectOptionColorList(selectedColor: state.option.color)); SelectOptionColorList(selectedColor: state.option.color),
);
} }
return SizedBox( return SizedBox(
@ -126,9 +127,11 @@ class _DeleteTag extends StatelessWidget {
class _OptionNameTextField extends StatelessWidget { class _OptionNameTextField extends StatelessWidget {
final String name; final String name;
final bool autoFocus; final bool autoFocus;
const _OptionNameTextField( const _OptionNameTextField({
{required this.name, required this.autoFocus, Key? key}) required this.name,
: super(key: key); required this.autoFocus,
Key? key,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -157,7 +160,9 @@ class SelectOptionColorList extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final cells = SelectOptionColorPB.values.map((color) { final cells = SelectOptionColorPB.values.map((color) {
return _SelectOptionColorCell( return _SelectOptionColorCell(
color: color, isSelected: selectedColor == color); color: color,
isSelected: selectedColor == color,
);
}).toList(); }).toList();
return Column( return Column(
@ -195,9 +200,11 @@ class SelectOptionColorList extends StatelessWidget {
class _SelectOptionColorCell extends StatelessWidget { class _SelectOptionColorCell extends StatelessWidget {
final SelectOptionColorPB color; final SelectOptionColorPB color;
final bool isSelected; final bool isSelected;
const _SelectOptionColorCell( const _SelectOptionColorCell({
{required this.color, required this.isSelected, Key? key}) required this.color,
: super(key: key); required this.isSelected,
Key? key,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -68,11 +68,13 @@ class _GridRowState extends State<GridRow> {
), ),
); );
return Row(children: [ return Row(
const _RowLeading(), children: [
content, const _RowLeading(),
const _RowTrailing(), content,
]); const _RowTrailing(),
],
);
}, },
), ),
), ),
@ -129,9 +131,11 @@ class _RowLeadingState extends State<_RowLeading> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const _InsertButton(), const _InsertButton(),
_MenuButton(openMenu: () { _MenuButton(
popoverController.show(); openMenu: () {
}), popoverController.show();
},
),
], ],
); );
} }
@ -216,12 +220,13 @@ class RowContent extends StatelessWidget {
!listEquals(previous.cells, current.cells), !listEquals(previous.cells, current.cells),
builder: (context, state) { builder: (context, state) {
return IntrinsicHeight( return IntrinsicHeight(
child: Row( child: Row(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: _makeCells(context, state.cellByFieldId), children: _makeCells(context, state.cellByFieldId),
)); ),
);
}, },
); );
} }

View File

@ -116,7 +116,10 @@ class _FilterTextFieldDelegate extends SliverPersistentHeaderDelegate {
@override @override
Widget build( Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) { BuildContext context,
double shrinkOffset,
bool overlapsContent,
) {
return Container( return Container(
padding: const EdgeInsets.only(top: 4), padding: const EdgeInsets.only(top: 4),
height: fixHeight, height: fixHeight,

View File

@ -76,13 +76,15 @@ class _SortList extends StatelessWidget {
return BlocBuilder<SortEditorBloc, SortEditorState>( return BlocBuilder<SortEditorBloc, SortEditorState>(
builder: (context, state) { builder: (context, state) {
final List<Widget> children = state.sortInfos final List<Widget> children = state.sortInfos
.map((info) => Padding( .map(
padding: const EdgeInsets.symmetric(vertical: 6), (info) => Padding(
child: _SortItem( padding: const EdgeInsets.symmetric(vertical: 6),
sortInfo: info, child: _SortItem(
popoverMutex: popoverMutex, sortInfo: info,
), popoverMutex: popoverMutex,
)) ),
),
)
.toList(); .toList();
return Column( return Column(

Some files were not shown because too many files have changed in this diff Show More