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
commit c009347735
683 changed files with 4329 additions and 39219 deletions

View File

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

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
working-directory: frontend/appflowy_flutter
run: flutter analyze
run: |
flutter analyze .
- name: Run Flutter unit tests
working-directory: frontend

View File

@ -22,7 +22,7 @@ jobs:
tests:
strategy:
matrix:
os: [macos-latest]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
@ -105,11 +105,14 @@ jobs:
working-directory: frontend/appflowy_flutter
run: |
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
flutter test integration_test/runner.dart -d macOS --coverage
flutter test integration_test/runner.dart -d macOS --coverage --verbose
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
shell: bash

View File

@ -205,12 +205,17 @@ script = [
]
script_runner = "@duckscript"
[env.test-macos]
[env.test-macos-x86_64]
TEST_CRATE_TYPE = "cdylib"
TEST_LIB_EXT = "dylib"
# For the moment, the DynamicLibrary only supports open x86_64 architectures binary.
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]
TEST_CRATE_TYPE = "cdylib"
TEST_LIB_EXT = "so"

View File

@ -14,9 +14,7 @@ analyzer:
exclude:
- "**/*.g.dart"
- "**/*.freezed.dart"
- "packages/appflowy_editor/**"
- "packages/editor/**"
# - "packages/flowy_infra_ui/**"
linter:
# The lint rules applied to this project can be customized in the
# 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
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
- require_trailing_commas
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

@ -1,112 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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>
<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>

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -1,150 +1,159 @@
{
"appName": "AppFlowy",
"defaultUsername": "Ich",
"welcomeText": "Willkommen bei @:appName",
"githubStarText": "GitHub Star vergeben",
"subscribeNewsletterText": "Abonniere den Newsletter",
"letsGoButtonText": "Los geht's",
"title": "Titel",
"signUp": {
"buttonText": "Registrieren",
"title": "Registriere dich bei @:appName",
"getStartedText": "Erste Schritte",
"emptyPasswordError": "Passwort darf nicht leer sein",
"repeatPasswordEmptyError": "Passwortwiederholung darf nicht leer sein",
"unmatchedPasswordError": "Passwörter stimmen nicht überein",
"alreadyHaveAnAccount": "Bereits registriert?",
"emailHint": "E-Mail",
"passwordHint": "Passwort",
"repeatPasswordHint": "Wiederhole Passwort"
},
"signIn": {
"loginTitle": "Bei @:appName einloggen",
"loginButtonText": "Anmelden",
"buttonText": "Anmelden",
"forgotPassword": "Passwort vergessen?",
"emailHint": "E-Mail",
"passwordHint": "Passwort",
"dontHaveAnAccount": "Du besitzt noch kein Konto?",
"repeatPasswordEmptyError": "Passwortwiederholung darf nicht leer sein",
"unmatchedPasswordError": "Passwörter stimmen nicht überein"
},
"workspace": {
"create": "Arbeitsbereich erstellen",
"hint": "Arbeitsbereich",
"notFoundError": "Arbeitsbereich nicht gefunden"
},
"shareAction": {
"buttonText": "Teilen",
"workInProgress": "Demnächst verfügbar",
"markdown": "Markdown",
"copyLink": "Link kopieren"
},
"disclosureAction": {
"rename": "Umbenennen",
"delete": "Löschen",
"duplicate": "Duplizieren"
},
"blankPageTitle": "Leere Seite",
"newPageText": "Neue Seite",
"trash": {
"text": "Papierkorb",
"restoreAll": "Alles wiederherstellen",
"deleteAll": "Alles löschen",
"pageHeader": {
"fileName": "Dateiname",
"lastModified": "Letzte Änderung",
"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"
"appName": "AppFlowy",
"defaultUsername": "Ich",
"welcomeText": "Willkommen bei @:appName",
"githubStarText": "GitHub Star vergeben",
"subscribeNewsletterText": "Abonniere den Newsletter",
"letsGoButtonText": "Los geht's",
"title": "Titel",
"signUp": {
"buttonText": "Registrieren",
"title": "Registriere dich bei @:appName",
"getStartedText": "Erste Schritte",
"emptyPasswordError": "Passwort darf nicht leer sein",
"repeatPasswordEmptyError": "Passwortwiederholung darf nicht leer sein",
"unmatchedPasswordError": "Passwörter stimmen nicht überein",
"alreadyHaveAnAccount": "Bereits registriert?",
"emailHint": "E-Mail",
"passwordHint": "Passwort",
"repeatPasswordHint": "Wiederhole Passwort"
},
"signIn": {
"loginTitle": "Bei @:appName einloggen",
"loginButtonText": "Anmelden",
"buttonText": "Anmelden",
"forgotPassword": "Passwort vergessen?",
"emailHint": "E-Mail",
"passwordHint": "Passwort",
"dontHaveAnAccount": "Du besitzt noch kein Konto?",
"repeatPasswordEmptyError": "Passwortwiederholung darf nicht leer sein",
"unmatchedPasswordError": "Passwörter stimmen nicht überein"
},
"workspace": {
"create": "Arbeitsbereich erstellen",
"hint": "Arbeitsbereich",
"notFoundError": "Arbeitsbereich nicht gefunden"
},
"shareAction": {
"buttonText": "Teilen",
"workInProgress": "Demnächst verfügbar",
"markdown": "Markdown",
"copyLink": "Link kopieren"
},
"disclosureAction": {
"rename": "Umbenennen",
"delete": "Löschen",
"duplicate": "Duplizieren"
},
"blankPageTitle": "Leere Seite",
"newPageText": "Neue Seite",
"trash": {
"text": "Papierkorb",
"restoreAll": "Alles wiederherstellen",
"deleteAll": "Alles löschen",
"pageHeader": {
"fileName": "Dateiname",
"lastModified": "Letzte Änderung",
"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"
},
"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",
"dateFormatLocal": "Month/Day/Year",
"dateFormatUS": "Year/Month/Day",
"dateFormatDayMonthYear": "Day/Month/Year",
"timeFormat": "Time format",
"invalidTimeFormat": "Invalid format",
"timeFormatTwelveHour": "12 hour",
@ -290,7 +291,7 @@
"optionTitle": "Options",
"addOption": "Add option",
"editProperty": "Edit property",
"newColumn": "New column",
"newProperty": "New property",
"deleteFieldPromptMessage": "Are you sure? This property will be deleted"
},
"sort": {
@ -383,7 +384,9 @@
"pickFromFiles": "Pick from files",
"couldNotFetchImage": "Could not fetch image",
"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"
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -1,150 +1,149 @@
{
"appName": "AppFlowy",
"defaultUsername": "Me",
"welcomeText": "Bem vindo ao @:appName",
"githubStarText": "Star on GitHub",
"subscribeNewsletterText": "Inscreve-te ao Newsletter",
"letsGoButtonText": "Bora",
"title": "Título",
"signUp": {
"buttonText": "Inscreve-te",
"title": "Inscreve-te ao @:appName",
"getStartedText": "Começar",
"emptyPasswordError": "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.",
"alreadyHaveAnAccount": "Já possuis uma conta?",
"emailHint": "Email",
"passwordHint": "Password",
"repeatPasswordHint": "Confirma a tua password"
},
"signIn": {
"loginTitle": "Entre no @:appName",
"loginButtonText": "Login",
"buttonText": "Entre",
"forgotPassword": "Esqueceste-te da tua palavra-passe?",
"emailHint": "Email",
"passwordHint": "Palavra-passe",
"dontHaveAnAccount": "Não possuis uma conta?",
"repeatPasswordEmptyError": "Confirmar a palavra-passe não pode estar em branco.",
"unmatchedPasswordError": "As palavras-passes não conferem."
},
"workspace": {
"create": "Cria um ambiente de trabalho",
"hint": "ambiente de trabalho",
"notFoundError": "Ambiente de trabalho não encontrada"
},
"shareAction": {
"buttonText": "Partilhar",
"workInProgress": "Em breve",
"markdown": "Markdown",
"copyLink": "Copiar o link"
},
"disclosureAction": {
"rename": "Renomear",
"delete": "Apagar",
"duplicate": "Duplicar"
},
"blankPageTitle": "Página em branco",
"newPageText": "Nova página",
"trash": {
"text": "Lixo",
"restoreAll": "Restaurar todos",
"deleteAll": "Apagar todos",
"pageHeader": {
"fileName": "Nome do ficheiro",
"lastModified": "Última modificação",
"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"
"appName": "AppFlowy",
"defaultUsername": "Me",
"welcomeText": "Bem vindo ao @:appName",
"githubStarText": "Star on GitHub",
"subscribeNewsletterText": "Inscreve-te ao Newsletter",
"letsGoButtonText": "Bora",
"title": "Título",
"signUp": {
"buttonText": "Inscreve-te",
"title": "Inscreve-te ao @:appName",
"getStartedText": "Começar",
"emptyPasswordError": "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.",
"alreadyHaveAnAccount": "Já possuis uma conta?",
"emailHint": "Email",
"passwordHint": "Password",
"repeatPasswordHint": "Confirma a tua password"
},
"signIn": {
"loginTitle": "Entre no @:appName",
"loginButtonText": "Login",
"buttonText": "Entre",
"forgotPassword": "Esqueceste-te da tua palavra-passe?",
"emailHint": "Email",
"passwordHint": "Palavra-passe",
"dontHaveAnAccount": "Não possuis uma conta?",
"repeatPasswordEmptyError": "Confirmar a palavra-passe não pode estar em branco.",
"unmatchedPasswordError": "As palavras-passes não conferem."
},
"workspace": {
"create": "Cria um ambiente de trabalho",
"hint": "ambiente de trabalho",
"notFoundError": "Ambiente de trabalho não encontrada"
},
"shareAction": {
"buttonText": "Partilhar",
"workInProgress": "Em breve",
"markdown": "Markdown",
"copyLink": "Copiar o link"
},
"disclosureAction": {
"rename": "Renomear",
"delete": "Apagar",
"duplicate": "Duplicar"
},
"blankPageTitle": "Página em branco",
"newPageText": "Nova página",
"trash": {
"text": "Lixo",
"restoreAll": "Restaurar todos",
"deleteAll": "Apagar todos",
"pageHeader": {
"fileName": "Nome do ficheiro",
"lastModified": "Última modificação",
"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"
}
}

View File

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

View File

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

View File

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

View File

@ -35,7 +35,9 @@ void main() {
setUpAll(() async => await service.setUpAll());
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();
expect(find.byType(AppFlowyBoard), findsOneWidget);
});

View File

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

View File

@ -61,6 +61,8 @@ class TestWorkspaceService {
InputFileStream(await workspace.zip.then((value) => value.path));
final archive = ZipDecoder().decodeBuffer(inputStream);
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;
class FlowyTestKeyboard {
static Future<void> simulateKeyDownEvent(List<LogicalKeyboardKey> keys,
{required flutter_test.WidgetTester tester}) async {
static Future<void> simulateKeyDownEvent(
List<LogicalKeyboardKey> keys, {
required flutter_test.WidgetTester tester,
}) async {
for (final LogicalKeyboardKey key in keys) {
await flutter_test.simulateKeyDownEvent(key);
await tester.pumpAndSettle();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,16 +7,20 @@ class NumberFormatBloc extends Bloc<NumberFormatEvent, NumberFormatState> {
NumberFormatBloc() : super(NumberFormatState.initial()) {
on<NumberFormatEvent>(
(event, emit) async {
event.map(setFilter: (_SetFilter value) {
final List<NumberFormat> formats = List.from(NumberFormat.values);
if (value.filter.isNotEmpty) {
formats.retainWhere((element) => element
.title()
.toLowerCase()
.contains(value.filter.toLowerCase()));
}
emit(state.copyWith(formats: formats, filter: value.filter));
});
event.map(
setFilter: (_SetFilter value) {
final List<NumberFormat> formats = List.from(NumberFormat.values);
if (value.filter.isNotEmpty) {
formats.retainWhere(
(element) => element
.title()
.toLowerCase()
.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() =
_EndAddingOption;
const factory SelectOptionTypeOptionEvent.updateOption(
SelectOptionPB option) = _UpdateOption;
SelectOptionPB option,
) = _UpdateOption;
const factory SelectOptionTypeOptionEvent.deleteOption(
SelectOptionPB option) = _DeleteOption;
SelectOptionPB option,
) = _DeleteOption;
}
@freezed

View File

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

View File

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

View File

@ -97,7 +97,8 @@ class FilterBackendService {
filter.end = $fixnum.Int64(end);
} else {
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;
DatabaseCalendarLayoutListener(this.viewId);
void start(
{required void Function(NewLayoutFieldValue) onCalendarLayoutChanged}) {
void start({
required void Function(NewLayoutFieldValue) onCalendarLayoutChanged,
}) {
_newLayoutFieldNotifier?.addPublishListener(onCalendarLayoutChanged);
_listener = DatabaseNotificationListener(
objectId: viewId,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -41,44 +41,46 @@ class GroupController {
}
void startListening() {
_listener.start(onGroupChanged: (result) {
result.fold(
(GroupRowsNotificationPB changeset) {
for (final deletedRow in changeset.deletedRows) {
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);
_listener.start(
onGroupChanged: (result) {
result.fold(
(GroupRowsNotificationPB changeset) {
for (final deletedRow in changeset.deletedRows) {
group.rows.removeWhere((rowPB) => rowPB.id == deletedRow);
delegate.removeRow(group, deletedRow);
}
if (insertedRow.isNew) {
delegate.addNewRow(group, insertedRow.row, index);
} else {
delegate.insertRow(group, insertedRow.row, index);
}
}
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);
}
for (final updatedRow in changeset.updatedRows) {
final index = group.rows.indexWhere(
(rowPB) => rowPB.id == updatedRow.id,
);
if (index != -1) {
group.rows[index] = updatedRow;
delegate.updateRow(group, updatedRow);
if (insertedRow.isNew) {
delegate.addNewRow(group, insertedRow.row, index);
} else {
delegate.insertRow(group, insertedRow.row, index);
}
}
}
},
(err) => Log.error(err),
);
});
for (final updatedRow in changeset.updatedRows) {
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 {

View File

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

View File

@ -80,8 +80,8 @@ class _BoardContentState extends State<BoardContent> {
late AppFlowyBoardScrollController scrollManager;
final cardConfiguration = CardConfiguration<String>();
final config = AppFlowyBoardConfig(
groupBackgroundColor: HexColor.fromHex('#F7F8FC'),
final config = const AppFlowyBoardConfig(
groupBackgroundColor: Color(0xffF7F8FC),
);
@override
@ -292,9 +292,10 @@ class _BoardContentState extends State<BoardContent> {
color: Theme.of(context).dividerColor,
width: 1.0,
);
final isLightMode = Theme.of(context).brightness == Brightness.light;
return BoxDecoration(
color: Theme.of(context).colorScheme.surface,
border: Border.fromBorderSide(borderSide),
border: isLightMode ? Border.fromBorderSide(borderSide) : null,
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? widget;
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:easy_localization/easy_localization.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/scrolling/styled_list.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
@ -104,8 +105,12 @@ class _SettingItem extends StatelessWidget {
return SizedBox(
height: 30,
child: FlowyButton(
hoverColor: AFThemeExtension.of(context).lightGreyHover,
isSelected: isSelected,
text: FlowyText.medium(action.title()),
text: FlowyText.medium(
action.title(),
color: AFThemeExtension.of(context).textColor,
),
onTap: () {
context
.read<BoardSettingBloc>()

View File

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

View File

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

View File

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

View File

@ -62,30 +62,32 @@ class CalendarDayCard extends StatelessWidget {
);
}).toList();
final child = Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
_Header(
date: date,
isInMonth: isInMonth,
isToday: isToday,
onCreate: () => onCreateEvent(date),
),
VSpace(GridSize.typeOptionSeparatorHeight),
Flexible(
child: ListView.separated(
itemBuilder: (BuildContext context, int index) {
return children[index];
},
itemCount: children.length,
separatorBuilder: (BuildContext context, int index) =>
VSpace(GridSize.typeOptionSeparatorHeight),
),
),
],
));
final child = Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: _Header(
date: date,
isInMonth: isInMonth,
isToday: isToday,
onCreate: () => onCreateEvent(date),
),
),
VSpace(GridSize.typeOptionSeparatorHeight),
Flexible(
child: ListView.separated(
itemBuilder: (BuildContext context, int index) {
return children[index];
},
itemCount: children.length,
padding: const EdgeInsets.symmetric(horizontal: 8.0),
separatorBuilder: (BuildContext context, int index) =>
VSpace(GridSize.typeOptionSeparatorHeight),
),
),
],
);
return Container(
color: backgroundColor,
@ -93,7 +95,10 @@ class CalendarDayCard extends StatelessWidget {
cursor: SystemMouseCursors.click,
onEnter: (p) => notifyEnter(context, true),
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,
child: Container(
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,
),
),

View File

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

View File

@ -18,6 +18,8 @@ import 'package:protobuf/protobuf.dart';
import 'calendar_setting.dart';
/// Widget that displays a list of settings that alters the appearance of the
/// calendar
class CalendarLayoutSetting extends StatefulWidget {
final CalendarSettingContext settingContext;
final Function(CalendarLayoutSettingsPB? layoutSettings) onUpdated;
@ -96,18 +98,15 @@ class _CalendarLayoutSettingState extends State<CalendarLayoutSetting> {
fieldId: settings.layoutFieldId,
popoverMutex: popoverMutex,
onUpdated: (fieldId) {
_updateLayoutSettings(context,
onUpdated: widget.onUpdated, layoutFieldId: fieldId);
_updateLayoutSettings(
context,
onUpdated: widget.onUpdated,
layoutFieldId: fieldId,
);
},
);
default:
return ShowWeekends(
showWeekends: settings.showWeekends,
onUpdated: (showWeekends) {
_updateLayoutSettings(context,
onUpdated: widget.onUpdated, showWeekends: showWeekends);
},
);
return const SizedBox();
}
}).toList();
@ -129,7 +128,8 @@ class _CalendarLayoutSettingState extends State<CalendarLayoutSetting> {
}
List<CalendarLayoutSettingAction> _availableCalendarSettings(
CalendarLayoutSettingsPB layoutSettings) {
CalendarLayoutSettingsPB layoutSettings,
) {
List<CalendarLayoutSettingAction> settings = [
CalendarLayoutSettingAction.layoutField,
// CalendarLayoutSettingAction.layoutType,
@ -220,8 +220,9 @@ class LayoutDateField extends StatelessWidget {
popupBuilder: (context) {
return BlocProvider(
create: (context) => getIt<DatabasePropertyBloc>(
param1: viewId, param2: fieldController)
..add(const DatabasePropertyEvent.initial()),
param1: viewId,
param2: fieldController,
)..add(const DatabasePropertyEvent.initial()),
child: BlocBuilder<DatabasePropertyBloc, DatabasePropertyState>(
builder: (context, state) {
final items = state.fieldContexts
@ -264,7 +265,8 @@ class LayoutDateField extends StatelessWidget {
child: FlowyButton(
margin: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 10.0),
text: FlowyText.medium(
LocaleKeys.calendar_settings_layoutDateField.tr()),
LocaleKeys.calendar_settings_layoutDateField.tr(),
),
),
),
);
@ -368,7 +370,8 @@ class FirstDayOfWeek extends StatelessWidget {
child: FlowyButton(
margin: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 10.0),
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';
/// The highest-level widget shown in the popover triggered by clicking the
/// "Settings" button. By default, shows [AllCalendarSettings] but upon
/// selecting a category, replaces contents with contents of the submenu.
/// "Settings" button. Shows [AllCalendarSettings] by default, but replaces its
/// contents with the submenu when a category is selected.
class CalendarSetting extends StatelessWidget {
final CalendarSettingContext settingContext;
final CalendarLayoutSettingsPB? layoutSettings;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -95,17 +95,20 @@ class _GridHeaderCellContainer extends StatelessWidget {
width: 1.0,
);
final decoration = BoxDecoration(
border: Border(
top: borderSide,
right: borderSide,
bottom: borderSide,
));
border: Border(
top: borderSide,
right: borderSide,
bottom: borderSide,
),
);
return Container(
width: width,
decoration: decoration,
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 FieldPB field;
final int? maxLines;
final BorderRadius? radius;
const FieldCellButton({
required this.field,
required this.onTap,
this.maxLines = 1,
this.radius = BorderRadius.zero,
Key? key,
}) : super(key: key);
@ -169,7 +174,7 @@ class FieldCellButton extends StatelessWidget {
leftIcon: FlowySvg(
name: field.fieldType.iconName(),
),
radius: BorderRadius.zero,
radius: radius,
text: FlowyText.medium(
text,
maxLines: maxLines,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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