Compare commits

..
3 Commits
Author SHA1 Message Date
Lucas.Xu a2e9d63f63 chore: bump version 0.1.4 (#2455) 2023-05-04 20:05:23 +08:00
Richard Shiue 5d0a349236 fix: default include time (#2444)
* fix: default include time

* chore: clarify logic and add comments
2023-05-04 12:41:48 +08:00
Richard ShiueandNathan.fooo db8d030a85 feat: show unscheduled events in calendar toolbar (#2411)
* refactor: use same show row detail function

* fix: adjust popover offset

* feat: show unscheduled events in toolbar

* chore: apply suggestions from Xazin

* refactor: refactor list item into separate widget

---------

Co-authored-by: Nathan.fooo <86001920+appflowy@users.noreply.github.com>
2023-05-04 11:05:17 +08:00
1523 changed files with 60942 additions and 57785 deletions

No files matched your search

+4 -21
View File
@@ -4,26 +4,22 @@ on:
push:
branches:
- "main"
- "develop"
- "release/*"
paths:
- ".github/workflows/flutter_ci.yaml"
- "frontend/**"
- "!frontend/appflowy_tauri/**"
pull_request:
branches:
- "main"
- "develop"
- "release/*"
paths:
- ".github/workflows/flutter_ci.yaml"
- "frontend/**"
- "!frontend/appflowy_tauri/**"
env:
FLUTTER_VERSION: "3.10.1"
RUST_TOOLCHAIN: "1.70"
FLUTTER_VERSION: "3.7.5"
RUST_TOOLCHAIN: "1.65"
jobs:
build:
@@ -66,17 +62,12 @@ jobs:
- uses: Swatinem/rust-cache@v2
with:
prefix-key: ${{ matrix.os }}
workspaces: |
frontend/rust-lib
- uses: davidB/rust-cargo-make@v1
with:
version: '0.36.6'
prefix-key: ${{ matrix.job.os }}
- name: Install prerequisites
working-directory: frontend
run: |
cargo install --force cargo-make
cargo install --force duckscript_cli
if [ "$RUNNER_OS" == "Linux" ]; then
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
@@ -99,7 +90,6 @@ jobs:
elif [ "$RUNNER_OS" == "macOS" ]; then
flutter config --enable-macos-desktop
elif [ "$RUNNER_OS" == "Windows" ]; then
git config --system core.longpaths true
flutter config --enable-windows-desktop
fi
shell: bash
@@ -118,10 +108,3 @@ jobs:
working-directory: frontend
run: |
cargo make dart_unit_test
- uses: codecov/codecov-action@v3
with:
name: appflowy
flags: appflowy_flutter_unit_test
fail_ci_if_error: true
verbose: true
+10 -13
View File
@@ -6,18 +6,14 @@ on:
- "main"
- "release/*"
paths:
- ".github/workflows/flutter_ci.yaml"
- "frontend/**"
- "!frontend/appflowy_tauri/**"
- "frontend/appflowy_flutter/**"
pull_request:
branches:
- "main"
- "release/*"
paths:
- ".github/workflows/flutter_ci.yaml"
- "frontend/**"
- "!frontend/appflowy_tauri/**"
- "frontend/appflowy_flutter/**"
env:
CARGO_TERM_COLOR: always
@@ -39,7 +35,7 @@ jobs:
- uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: "3.10.1"
flutter-version: "3.7.5"
cache: true
- name: Cache Cargo
@@ -120,9 +116,10 @@ jobs:
fi
shell: bash
- uses: codecov/codecov-action@v3
with:
name: appflowy
flags: appflowy_flutter_integrateion_test
fail_ci_if_error: true
verbose: true
# - uses: codecov/codecov-action@v3
# with:
# name: appflowy
# flags: appflowy
# env_vars: ${{ matrix.os }}
# fail_ci_if_error: true
# verbose: true
+7 -3
View File
@@ -6,8 +6,8 @@ on:
- "*"
env:
FLUTTER_VERSION: "3.10.1"
RUST_TOOLCHAIN: "1.70"
FLUTTER_VERSION: "3.7.5"
RUST_TOOLCHAIN: "1.65"
jobs:
create-release:
@@ -136,7 +136,11 @@ jobs:
fail-fast: false
matrix:
job:
- { target: x86_64-apple-darwin, os: macos-11, extra-build-args: "" }
- {
target: x86_64-apple-darwin,
os: macos-10.15,
extra-build-args: "",
}
steps:
- name: Checkout source code
uses: actions/checkout@v3
+8 -12
View File
@@ -4,7 +4,6 @@ on:
push:
branches:
- "main"
- "develop"
- "release/*"
paths:
- "frontend/rust-lib/**"
@@ -13,7 +12,6 @@ on:
pull_request:
branches:
- "main"
- "develop"
- "release/*"
paths:
- "frontend/rust-lib/**"
@@ -21,8 +19,8 @@ on:
env:
CARGO_TERM_COLOR: always
RUST_TOOLCHAIN: "1.70"
FLUTTER_VERSION: "3.10.1"
RUST_TOOLCHAIN: "1.65"
FLUTTER_VERSION: "3.7.5"
jobs:
test-on-ubuntu:
@@ -56,19 +54,13 @@ jobs:
- uses: Swatinem/rust-cache@v2
with:
prefix-key: 'ubuntu-latest'
workspaces: |
frontend/rust-lib
prefix-key: ${{ matrix.job.os }}
- name: Build FlowySDK
working-directory: frontend
run: |
cargo make --profile development-linux-x86_64 appflowy-core-dev
- name: Run rust-lib tests
working-directory: frontend/rust-lib
run: RUST_LOG=info RUST_BACKTRACE=1 cargo test --no-default-features --features="rev-sqlite"
- name: rustfmt rust-lib
run: cargo fmt --all -- --check
working-directory: frontend/rust-lib/
@@ -77,12 +69,16 @@ jobs:
run: cargo clippy --features="rev-sqlite"
working-directory: frontend/rust-lib
- name: Run rust-lib tests
working-directory: frontend/rust-lib
run: RUST_LOG=info cargo test --no-default-features --features="sync,rev-sqlite"
- name: rustfmt shared-lib
run: cargo fmt --all -- --check
working-directory: shared-lib
- name: clippy shared-lib
run: cargo clippy -- -D warnings
run: cargo clippy -- -D warnings
working-directory: shared-lib
- name: Run shared-lib tests
+2 -2
View File
@@ -11,8 +11,8 @@ on:
env:
CARGO_TERM_COLOR: always
FLUTTER_VERSION: "3.10.1"
RUST_TOOLCHAIN: "1.70"
FLUTTER_VERSION: "3.7.5"
RUST_TOOLCHAIN: "1.65"
jobs:
tests:
+15 -16
View File
@@ -6,17 +6,16 @@ on:
- "frontend/appflowy_tauri/**"
env:
NODE_VERSION: "18.16.0"
PNPM_VERSION: "8.5.0"
RUST_TOOLCHAIN: "1.70"
NODE_VERSION: "16"
RUST_TOOLCHAIN: "1.65"
jobs:
tauri-build:
strategy:
fail-fast: false
matrix:
# platform: [macos-latest, ubuntu-latest, windows-latest]
platform: [ubuntu-latest]
# platform: [macos-latest, ubuntu-20.04, windows-latest]
platform: [macos-latest]
runs-on: ${{ matrix.platform }}
steps:
@@ -36,10 +35,7 @@ jobs:
- uses: Swatinem/rust-cache@v2
with:
prefix-key: 'ubuntu-latest-tauri'
workspaces: |
frontend/rust-lib
frontend/appflowy_tauri/src-tauri
prefix-key: ${{ matrix.platform }}
- name: install dependencies (windows only)
if: matrix.platform == 'windows-latest'
@@ -49,17 +45,15 @@ jobs:
cargo install --force duckscript_cli
vcpkg integrate install
cargo make appflowy-tauri-deps-tools
npm install -g pnpm@${{ env.PNPM_VERSION }}
- name: install dependencies (ubuntu only)
if: matrix.platform == 'ubuntu-latest'
if: matrix.platform == 'ubuntu-20.04'
working-directory: frontend
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
cargo install --force cargo-make
cargo make appflowy-tauri-deps-tools
npm install -g pnpm@${{ env.PNPM_VERSION }}
- name: install dependencies (macOS only)
if: matrix.platform == 'macos-latest'
@@ -67,16 +61,21 @@ jobs:
run: |
cargo install --force cargo-make
cargo make appflowy-tauri-deps-tools
npm install -g pnpm@${{ env.PNPM_VERSION }}
# - name: lint
# working-directory: frontend/appflowy_tauri
# run: |
# yarn --frozen-lockfile
# yarn test:prettier
# yarn test:code
- name: build
working-directory: frontend/appflowy_tauri
run: |
mkdir dist
pnpm install
cargo make --cwd .. tauri_build
pnpm test:errors
npm install
cargo make --cwd .. tauri_build
yarn && yarn build
- uses: tauri-apps/tauri-action@v0
env:
-2
View File
@@ -35,5 +35,3 @@ pubspec.lock
.githooks/gitlint
.githooks/gitlint.exe
.fvm/
**/AppFlowy-Collab/
-56
View File
@@ -1,61 +1,5 @@
# Release Notes
## Version 0.2.1 - 06/11/2023
### New Features
- Added support for creating or referencing a calendar in the document
- Added `+` icon in grid's add field
-
### Other Updates
- Added vertical padding for progress bar
- Hide url cell accessory when the content is empty
### Bug fixes:
- Fixed unable to export markdown
- Fixed adding vertical padding for progress bar
- Fixed database view didn't update after the database layout changed.
## Version 0.2.0 - 06/08/2023
### New Features
- Improved checklists to support each cell having its own list
- Drag and drop calendar events
- Switch layouts (calendar, grid, kanban) of a database
- New database properties: 'Updated At' and 'Created At'
- Enabled hiding properties on the row detail page
- Added support for reordering and saving row order in different database views.
- Enabled each database view to have its own settings, including filter and sort options
- Added support to convert `“` (double quote) into a block quote
- Added support to convert `***` (three stars) into a divider
- Added support for an 'Add' button to insert a paragraph in a document and display the slash menu
- Added support for an 'Option' button to delete, duplicate, and customize block actions
### Other Updates
- Added support for importing v0.1.x documents and databases
- Added support for database import and export to CSV
- Optimized scroll behavior in documents.
- Redesigned the launch page
### Bug fixes
- Fixed bugs related to numbers
- Fixed issues with referenced databases in documents
- Fixed menu overflow issues in documents
### Data migration
The data format of this version is not compatible with previous versions. Therefore, to migrate your data to the new version, you need to use the export and import functions. Please follow the guide to learn how to export and import your data.
#### Export files in v0.1.6
https://github.com/AppFlowy-IO/AppFlowy/assets/11863087/0c89bf2b-cd97-4a7b-b627-59df8d2967d9
#### Import files in v0.2.0
https://github.com/AppFlowy-IO/AppFlowy/assets/11863087/7b392f35-4972-497a-8a7f-f38efced32e2
## Version 0.1.5 - 11/05/2023
### Bug Fixes
- Fix: calendar dates don't match with weekdays.
- Fix: sort numbers in Grid.
## Version 0.1.4 - 04/05/2023
### New features
+3 -5
View File
@@ -23,11 +23,9 @@ You are in charge of your data and customizations.
<a href="https://twitter.com/appflowy"><b>Twitter</b></a>
</p>
<p align="center"><img src="https://user-images.githubusercontent.com/12026239/236664610-fc209a97-815e-4716-af07-d94a859d1907.png" alt="AppFlowy Docs & Notes & Wikis" width="1000px" /></p>
<p align="center"><img src="https://user-images.githubusercontent.com/12026239/236664628-5def2450-914a-4b2d-b907-92b7476b9863.png" alt="AppFlowy Databases for Tasks and Projects" width="1000px" /></p>
<p align="center"><img src="https://user-images.githubusercontent.com/12026239/236664642-22e26c1b-5eae-4635-9aa6-b12ecf1c3c46.png" alt="AppFlowy Kanban Board for To-Dos" width="1000px" /></p>
<p align="center"><img src="https://github.com/AppFlowy-IO/AppFlowy/assets/12026239/6be93d2b-a5c5-48a9-b7cf-c599d5f5140c" alt="AppFlowy Calendars for Plan and Manage Content" width="1000px" /></p>
<p align="center"><img src="https://user-images.githubusercontent.com/12026239/236664657-dc5291f3-67b0-4a43-a818-640e92735deb.png" alt="AppFlowy OpenAI GPT Writers" width="1000px" /></p>
<p align="center"><img src="https://user-images.githubusercontent.com/12026239/200787830-96be260b-d0a0-4152-864e-6730b19095cd.png" alt="The Open Source Alternative To Notion." width="1000px" /></p>
<p align="center"><img src="https://user-images.githubusercontent.com/12026239/174753177-98e4c899-2356-4137-bb42-374bba2b127b.png" alt="The Open Source Alternative To Notion." width="1000px" /></p>
<p align="center"><img src="https://user-images.githubusercontent.com/12026239/190650183-a940f1e0-a2c5-4797-ab3a-56758f6f696c.png" alt="The Open Source Alternative To Notion." width="1000px" /></p>
## User Installation
+2 -2
View File
@@ -13,7 +13,7 @@
"preLaunchTask": "AF: Build Appflowy Core",
"env": {
"RUST_LOG": "trace",
"RUST_BACKTRACE": 1
// "RUST_LOG": "debug"
},
"cwd": "${workspaceRoot}/appflowy_flutter"
},
@@ -24,7 +24,7 @@
"program": "./lib/main.dart",
"type": "dart",
"env": {
"RUST_LOG": "debug",
"RUST_LOG": "debug"
},
"cwd": "${workspaceRoot}/appflowy_flutter"
},
+2 -2
View File
@@ -118,7 +118,7 @@
{
"label": "AF: Generate Freezed Files",
"type": "shell",
"command": "dart run build_runner build -d",
"command": "flutter pub run build_runner build --delete-conflicting-outputs",
"options": {
"cwd": "${workspaceFolder}/appflowy_flutter"
}
@@ -180,7 +180,7 @@
{
"label": "AF: Tauri UI Build",
"type": "shell",
"command": "pnpm run build",
"command": "npm run build",
"options": {
"cwd": "${workspaceFolder}/appflowy_tauri"
}
+1 -5
View File
@@ -23,7 +23,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
CARGO_MAKE_CRATE_NAME = "dart-ffi"
LIB_NAME = "dart_ffi"
CURRENT_APP_VERSION = "0.2.1"
CURRENT_APP_VERSION = "0.1.4"
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite"
PRODUCT_NAME = "AppFlowy"
# CRATE_TYPE: https://doc.rust-lang.org/reference/linkage.html
@@ -59,7 +59,6 @@ BUILD_FLAG = "debug"
FLUTTER_OUTPUT_DIR = "Debug"
PRODUCT_EXT = "app"
BUILD_ARCHS = "arm64"
CRATE_TYPE = "staticlib"
[env.development-mac-x86_64]
RUST_LOG = "info"
@@ -69,7 +68,6 @@ BUILD_FLAG = "debug"
FLUTTER_OUTPUT_DIR = "Debug"
PRODUCT_EXT = "app"
BUILD_ARCHS = "x86_64"
CRATE_TYPE = "staticlib"
[env.production-mac-arm64]
BUILD_FLAG = "release"
@@ -79,7 +77,6 @@ FLUTTER_OUTPUT_DIR = "Release"
PRODUCT_EXT = "app"
APP_ENVIRONMENT = "production"
BUILD_ARCHS = "arm64"
CRATE_TYPE = "staticlib"
[env.production-mac-x86_64]
BUILD_FLAG = "release"
@@ -89,7 +86,6 @@ FLUTTER_OUTPUT_DIR = "Release"
PRODUCT_EXT = "app"
APP_ENVIRONMENT = "production"
BUILD_ARCHS = "x86_64"
CRATE_TYPE = "staticlib"
[env.development-windows-x86]
TARGET_OS = "windows"
-4
View File
@@ -68,7 +68,3 @@ windows/flutter/dart_ffi/
**/**/Brewfile.lock.json
**/.sandbox
**/.vscode/
*.env
coverage/
+2 -2
View File
@@ -1,7 +1,7 @@
.PHONY: freeze_build, free_watch
freeze_build:
dart run build_runner build -d
flutter pub run build_runner build --delete-conflicting-outputs
watch:
dart run build_runner watch
flutter pub run build_runner watch
+1 -1
View File
@@ -1,6 +1,6 @@
<h1 align="center" style="margin:0"> AppFlowy_Flutter</h1>
<div align="center">
<img src="https://img.shields.io/badge/Flutter-v3.10.1-blue"/>
<img src="https://img.shields.io/badge/Flutter-v3.7.5-blue"/>
<img src="https://img.shields.io/badge/Rust-v1.65-orange"/>
</div>
@@ -30,10 +30,6 @@ linter:
rules:
- require_trailing_commas
- prefer_final_fields
- prefer_final_in_for_each
- prefer_final_locals
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
@@ -1,10 +0,0 @@
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_10_2941)">
<path d="M17.9706 5.70199L17.9706 5.702L17.9712 5.70405L20.4218 13.6624C20.4393 13.7435 20.4508 13.8258 20.4561 13.9086V19.3106C20.4561 19.7769 20.078 20.1549 19.6117 20.1548H3.40062C2.93431 20.1549 2.55625 19.7769 2.55625 19.3106V13.9086C2.56155 13.8257 2.57301 13.7433 2.59054 13.6622L4.97263 5.70366C5.26597 4.72587 6.16595 4.05625 7.1868 4.05625L15.8285 4.05625C16.8329 4.05625 17.7119 4.7315 17.9706 5.70199ZM4.55716 12.99L4.53544 13.0624H4.61104H8.80431C9.02825 13.0624 9.24302 13.1514 9.40137 13.3097C9.55971 13.4681 9.64867 13.6828 9.64867 13.9068V14.8074V14.8636H9.70492H13.3074H13.3636V14.8074V13.9068C13.3636 13.6828 13.4526 13.4681 13.6109 13.3097C13.7693 13.1514 13.9841 13.0624 14.208 13.0624H18.4013H18.4769L18.4551 12.99L16.5342 6.5868C16.3843 6.08715 15.9244 5.74498 15.4028 5.74498H7.60955C7.0879 5.74498 6.62802 6.08715 6.47812 6.5868L4.55716 12.99ZM15.1086 14.7511H15.0524V14.8074V15.708C15.0524 15.8189 15.0305 15.9287 14.9881 16.0311C14.9457 16.1336 14.8835 16.2267 14.8051 16.3051C14.7267 16.3835 14.6336 16.4457 14.5311 16.4881C14.4287 16.5305 14.3189 16.5524 14.208 16.5524H8.80431C8.69343 16.5524 8.58363 16.5305 8.48118 16.4881C8.37874 16.4457 8.28566 16.3835 8.20725 16.3051C8.12885 16.2267 8.06665 16.1336 8.02422 16.0311C7.98178 15.9287 7.95994 15.8189 7.95994 15.708V14.8074V14.7511H7.90369H4.52623C4.3709 14.7511 4.24498 14.8771 4.24498 15.0324V18.1849C4.24498 18.3402 4.3709 18.4661 4.52623 18.4661H18.4861C18.6414 18.4661 18.7673 18.3402 18.7673 18.1849V15.0324C18.7673 14.8771 18.6414 14.7511 18.4861 14.7511H15.1086ZM8.80435 10.2481C8.58041 10.2481 8.36564 10.1591 8.20729 10.0007C8.04895 9.8424 7.95999 9.62763 7.95999 9.40369C7.95999 9.17975 8.04895 8.96498 8.20729 8.80663C8.36564 8.64829 8.58041 8.55933 8.80435 8.55933H14.208C14.432 8.55933 14.6468 8.64829 14.8051 8.80663C14.9635 8.96498 15.0524 9.17975 15.0524 9.40369C15.0524 9.62763 14.9635 9.8424 14.8051 10.0007C14.6468 10.1591 14.432 10.2481 14.208 10.2481H8.80435Z" fill="#333333" stroke="white" stroke-width="0.1125"/>
</g>
<defs>
<clipPath id="clip0_10_2941">
<rect width="18" height="16.2111" fill="white" transform="translate(2.5 4)"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

@@ -1,3 +0,0 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.7" d="M14.1579 9L14.6673 8.57546C14.5413 8.42426 14.3547 8.33684 14.1579 8.33684V9ZM16 11.2105L15.4905 11.6351C15.6165 11.7863 15.8032 11.8737 16 11.8737V11.2105ZM9.66316 12.6843C9.66316 12.318 9.36625 12.0211 9 12.0211C8.63375 12.0211 8.33684 12.318 8.33684 12.6843H9.66316ZM8.33684 17.1053C8.33684 17.4716 8.63375 17.7685 9 17.7685C9.36625 17.7685 9.66316 17.4716 9.66316 17.1053H8.33684ZM23.6632 12.6843C23.6632 12.318 23.3663 12.0211 23 12.0211C22.6337 12.0211 22.3368 12.318 22.3368 12.6843H23.6632ZM22.3368 17.1053C22.3368 17.4716 22.6337 17.7685 23 17.7685C23.3663 17.7685 23.6632 17.4716 23.6632 17.1053H22.3368ZM9.66316 9.73684C9.66316 9.69614 9.69614 9.66316 9.73684 9.66316V8.33684C8.96364 8.33684 8.33684 8.96364 8.33684 9.73684H9.66316ZM9.73684 9.66316H14.1579V8.33684H9.73684V9.66316ZM13.6484 9.42454L15.4905 11.6351L16.5094 10.786L14.6673 8.57546L13.6484 9.42454ZM16 11.8737H22.2631V10.5474H16V11.8737ZM22.2631 11.8737C22.3038 11.8737 22.3368 11.9067 22.3368 11.9474H23.6631C23.6631 11.1742 23.0363 10.5474 22.2631 10.5474V11.8737ZM22.3368 11.9474V21.5263H23.6631V11.9474H22.3368ZM22.3368 21.5263C22.3368 21.567 22.3038 21.6 22.2631 21.6V22.9263C23.0363 22.9263 23.6631 22.2995 23.6631 21.5263H22.3368ZM22.2631 21.6H9.73684V22.9263H22.2631V21.6ZM9.73684 21.6C9.69614 21.6 9.66316 21.567 9.66316 21.5263H8.33684C8.33684 22.2995 8.96365 22.9263 9.73684 22.9263V21.6ZM9.66316 21.5263V9.73684H8.33684V21.5263H9.66316ZM22.9999 14.2317H9V15.558H22.9999V14.2317ZM8.33684 12.6843V17.1053H9.66316V12.6843H8.33684ZM22.3368 12.6843V17.1053H23.6632V12.6843H22.3368Z" fill="#333333"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

@@ -1,4 +0,0 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.7874 10.1975C22.48 10.8859 23.0235 11.6885 23.4041 12.5811C23.7967 13.5077 24 14.4863 24 15.499C24 16.5117 23.7987 17.4943 23.4061 18.4189C23.0256 19.3115 22.482 20.1141 21.7894 20.8025C21.0968 21.491 20.2895 22.0314 19.3915 22.4096C18.4614 22.8019 17.4729 23 16.4541 23C15.4354 23 14.4469 22.7999 13.5167 22.4096C12.6188 22.0314 11.8114 21.491 11.1189 20.8025C10.8833 20.5684 10.8833 20.1881 11.1189 19.954C11.3544 19.7198 11.7369 19.7198 11.9725 19.954C13.1684 21.1448 14.7589 21.7992 16.4521 21.7992C18.1453 21.7992 19.7338 21.1428 20.9317 19.954C22.1296 18.7652 22.792 17.1801 22.792 15.499C22.792 13.8179 22.1316 12.2368 20.9357 11.046C19.7398 9.85524 18.1493 9.2008 16.4561 9.2008C15.6971 9.2008 14.9562 9.33089 14.2516 9.59106C13.5711 9.84323 12.9429 10.2055 12.3852 10.6718C11.6041 11.3242 10.996 12.1508 10.6075 13.0734L11.2618 12.7231C11.5557 12.565 11.9222 12.6751 12.0792 12.9673C12.2383 13.2595 12.1275 13.6237 11.8336 13.7799L9.92094 14.8005C9.92094 14.8025 9.91892 14.8025 9.91892 14.8025C9.82832 14.8506 9.72967 14.8746 9.63303 14.8746C9.44378 14.8746 9.26258 14.7885 9.14581 14.6304C9.14455 14.6267 9.14249 14.6237 9.14064 14.621C9.13954 14.6194 9.13851 14.6179 9.13776 14.6164C9.13575 14.6124 9.13323 14.6089 9.13071 14.6054C9.1282 14.6019 9.12568 14.5984 9.12367 14.5944C9.11561 14.5824 9.10756 14.5704 9.10152 14.5584L8.07272 12.6551C7.91367 12.3629 8.0244 11.9987 8.31834 11.8426C8.61228 11.6845 8.97871 11.7945 9.13575 12.0867L9.45989 12.6871C9.91892 11.5584 10.6558 10.5457 11.6061 9.75317C12.2705 9.1988 13.0174 8.76451 13.8288 8.46631C14.6663 8.1561 15.5501 8 16.4521 8C17.4708 8 18.4594 8.20013 19.3895 8.59039C20.2875 8.96865 21.0948 9.50901 21.7874 10.1975ZM15.52 12.1206C15.52 11.7884 15.7898 11.5202 16.124 11.5202C16.4582 11.5202 16.728 11.7884 16.728 12.1206V15.2747H19.8244C20.1587 15.2747 20.4284 15.5429 20.4284 15.8751C20.4284 16.2073 20.1587 16.4755 19.8244 16.4755H16.1481C15.8441 16.4755 15.5925 16.2514 15.5502 15.9612C15.5301 15.9031 15.52 15.8411 15.52 15.7751V12.1206Z" fill="#333333" fill-opacity="0.7"/>
<path d="M23.4041 12.5811L23.4435 12.5643L23.4435 12.5642L23.4041 12.5811ZM21.7874 10.1975L21.7572 10.2279L21.7874 10.1975ZM23.4061 18.4189L23.4455 18.4358L23.4455 18.4357L23.4061 18.4189ZM19.3915 22.4096L19.3749 22.3701L19.3749 22.3701L19.3915 22.4096ZM13.5167 22.4096L13.5001 22.4491L13.5001 22.4491L13.5167 22.4096ZM11.1189 20.8025L11.1491 20.7721L11.1189 20.8025ZM11.1189 19.954L11.1491 19.9844L11.1189 19.954ZM11.9725 19.954L12.0027 19.9236L12.0027 19.9236L11.9725 19.954ZM20.9317 19.954L20.9619 19.9844L20.9317 19.954ZM20.9357 11.046L20.9055 11.0764L20.9357 11.046ZM14.2516 9.59106L14.2367 9.55086L14.2367 9.55087L14.2516 9.59106ZM12.3852 10.6718L12.4127 10.7047L12.4127 10.7047L12.3852 10.6718ZM10.6075 13.0734L10.568 13.0567L10.521 13.1683L10.6277 13.1112L10.6075 13.0734ZM11.2618 12.7231L11.282 12.7609L11.2821 12.7609L11.2618 12.7231ZM12.0792 12.9673L12.0415 12.9876L12.0416 12.9878L12.0792 12.9673ZM11.8336 13.7799L11.8135 13.742L11.8134 13.742L11.8336 13.7799ZM9.92094 14.8005L9.90076 14.7627L9.87808 14.7748V14.8005H9.92094ZM9.91892 14.8025L9.9189 14.7597L9.90825 14.7597L9.89885 14.7647L9.91892 14.8025ZM9.14581 14.6304L9.10311 14.6447L9.11134 14.6559L9.14581 14.6304ZM9.14064 14.621L9.10537 14.6453L9.10537 14.6453L9.14064 14.621ZM9.13776 14.6164L9.17605 14.5972L9.17604 14.5972L9.13776 14.6164ZM9.13071 14.6054L9.09591 14.6304L9.09591 14.6304L9.13071 14.6054ZM9.12367 14.5944L9.16225 14.575L9.15926 14.5705L9.12367 14.5944ZM9.10152 14.5584L9.13982 14.5391L9.13922 14.538L9.10152 14.5584ZM8.07272 12.6551L8.11042 12.6347L8.11036 12.6346L8.07272 12.6551ZM8.31834 11.8426L8.33844 11.8804L8.33864 11.8803L8.31834 11.8426ZM9.13575 12.0867L9.09799 12.107L9.09803 12.1071L9.13575 12.0867ZM9.45989 12.6871L9.42218 12.7075L9.46535 12.7875L9.49959 12.7033L9.45989 12.6871ZM11.6061 9.75317L11.6335 9.78608L11.6335 9.78608L11.6061 9.75317ZM13.8288 8.46631L13.8436 8.50654L13.8437 8.5065L13.8288 8.46631ZM19.3895 8.59039L19.4062 8.5509L19.4061 8.55087L19.3895 8.59039ZM16.728 15.2747H16.6851V15.3176H16.728V15.2747ZM15.5502 15.9612L15.5934 15.9549L15.5907 15.9471L15.5502 15.9612ZM23.4435 12.5642C23.0608 11.6666 22.5141 10.8594 21.8176 10.1671L21.7572 10.2279C22.4458 10.9124 22.9863 11.7104 23.3646 12.5979L23.4435 12.5642ZM24.0429 15.499C24.0429 14.4805 23.8383 13.4961 23.4435 12.5643L23.3646 12.5978C23.755 13.5192 23.9571 14.4922 23.9571 15.499H24.0429ZM23.4455 18.4357C23.8404 17.5058 24.0429 16.5174 24.0429 15.499H23.9571C23.9571 16.5059 23.757 17.4829 23.3666 18.4022L23.4455 18.4357ZM21.8196 20.8329C22.5161 20.1406 23.0628 19.3334 23.4455 18.4358L23.3667 18.4021C22.9883 19.2896 22.4478 20.0876 21.7592 20.7721L21.8196 20.8329ZM19.4082 22.4491C20.3112 22.0687 21.1231 21.5253 21.8196 20.8329L21.7592 20.7721C21.0705 21.4567 20.2678 21.994 19.3749 22.3701L19.4082 22.4491ZM16.4541 23.0429C17.4785 23.0429 18.4727 22.8436 19.4082 22.4491L19.3749 22.3701C18.4501 22.7601 17.4672 22.9571 16.4541 22.9571V23.0429ZM13.5001 22.4491C14.4355 22.8416 15.4297 23.0429 16.4541 23.0429V22.9571C15.4411 22.9571 14.4582 22.7581 13.5333 22.3701L13.5001 22.4491ZM11.0886 20.8329C11.7851 21.5253 12.5971 22.0687 13.5001 22.4491L13.5333 22.3701C12.6405 21.994 11.8377 21.4567 11.1491 20.7721L11.0886 20.8329ZM11.0886 19.9236C10.8362 20.1745 10.8362 20.582 11.0886 20.8329L11.1491 20.7721C10.9304 20.5547 10.9304 20.2018 11.1491 19.9844L11.0886 19.9236ZM12.0027 19.9236C11.7504 19.6728 11.3409 19.6728 11.0886 19.9236L11.1491 19.9844C11.3679 19.7668 11.7234 19.7668 11.9423 19.9844L12.0027 19.9236ZM16.4521 21.7563C14.7702 21.7563 13.1906 21.1064 12.0027 19.9236L11.9423 19.9843C13.1462 21.1832 14.7476 21.8421 16.4521 21.8421V21.7563ZM20.9015 19.9235C19.7116 21.1044 18.134 21.7563 16.4521 21.7563V21.8421C18.1566 21.8421 19.756 21.1811 20.9619 19.9844L20.9015 19.9235ZM22.7492 15.499C22.7492 17.1686 22.0914 18.7428 20.9015 19.9235L20.9619 19.9844C22.1679 18.7876 22.8349 17.1916 22.8349 15.499H22.7492ZM20.9055 11.0764C22.0934 12.2592 22.7492 13.8294 22.7492 15.499H22.8349C22.8349 13.8064 22.1699 12.2144 20.966 11.0157L20.9055 11.0764ZM16.4561 9.24366C18.1381 9.24366 19.7177 9.89365 20.9055 11.0764L20.966 11.0157C19.762 9.81683 18.1606 9.15794 16.4561 9.15794V9.24366ZM14.2664 9.63127C14.9662 9.37289 15.7021 9.24366 16.4561 9.24366V9.15794C15.6922 9.15794 14.9462 9.28889 14.2367 9.55086L14.2664 9.63127ZM12.4127 10.7047C12.9666 10.2415 13.5905 9.88174 14.2665 9.63125L14.2367 9.55087C13.5517 9.80472 12.9192 10.1694 12.3577 10.6389L12.4127 10.7047ZM10.647 13.09C11.0329 12.1737 11.6368 11.3527 12.4127 10.7047L12.3578 10.6389C11.5713 11.2957 10.9592 12.1278 10.568 13.0567L10.647 13.09ZM11.2416 12.6854L10.5872 13.0356L10.6277 13.1112L11.282 12.7609L11.2416 12.6854ZM12.117 12.947C11.9487 12.6339 11.5562 12.5161 11.2415 12.6854L11.2821 12.7609C11.5553 12.614 11.8957 12.7164 12.0415 12.9876L12.117 12.947ZM11.8537 13.8177C12.1685 13.6505 12.2874 13.2601 12.1168 12.9468L12.0416 12.9878C12.1891 13.2589 12.0865 13.597 11.8135 13.742L11.8537 13.8177ZM9.94111 14.8383L11.8538 13.8177L11.8134 13.74Line truncated
</svg>

Before

Width:  |  Height:  |  Size: 11 KiB

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Calendar / Calendar_Days">
<path id="Vector" d="M8 4H7.2002C6.08009 4 5.51962 4 5.0918 4.21799C4.71547 4.40973 4.40973 4.71547 4.21799 5.0918C4 5.51962 4 6.08009 4 7.2002V8M8 4H16M8 4V2M16 4H16.8002C17.9203 4 18.4796 4 18.9074 4.21799C19.2837 4.40973 19.5905 4.71547 19.7822 5.0918C20 5.5192 20 6.07899 20 7.19691V8M16 4V2M4 8V16.8002C4 17.9203 4 18.4801 4.21799 18.9079C4.40973 19.2842 4.71547 19.5905 5.0918 19.7822C5.5192 20 6.07899 20 7.19691 20H16.8031C17.921 20 18.48 20 18.9074 19.7822C19.2837 19.5905 19.5905 19.2842 19.7822 18.9079C20 18.4805 20 17.9215 20 16.8036V8M4 8H20M16 16H16.002L16.002 16.002L16 16.002V16ZM12 16H12.002L12.002 16.002L12 16.002V16ZM8 16H8.002L8.00195 16.002L8 16.002V16ZM16.002 12V12.002L16 12.002V12H16.002ZM12 12H12.002L12.002 12.002L12 12.002V12ZM8 12H8.002L8.00195 12.002L8 12.002V12Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

@@ -1,29 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M2990 5100 c-19 -10 -382 -366 -806 -792 l-771 -773 -42 -85 c-125
-256 -84 -501 120 -714 47 -50 91 -101 98 -113 14 -27 14 -116 0 -168 -57
-207 -310 -495 -535 -606 -73 -37 -92 -42 -190 -50 -60 -4 -145 -18 -189 -29
-312 -80 -560 -328 -646 -645 -32 -116 -32 -336 -1 -450 88 -318 330 -560 647
-646 115 -32 335 -32 450 0 263 71 474 247 591 494 45 95 71 203 83 337 7 90
15 122 40 176 149 318 583 627 777 554 15 -6 68 -50 119 -98 214 -205 459
-246 715 -121 85 42 85 42 849 803 420 418 776 781 792 805 34 53 37 97 10
149 -24 46 -1934 1954 -1978 1976 -42 21 -90 20 -133 -4z m920 -1195 l845
-845 -318 -317 -317 -318 -847 847 -848 848 315 315 c173 173 317 315 320 315
3 0 385 -380 850 -845z m-282 -1972 c-320 -320 -330 -327 -458 -328 -100 0
-145 23 -264 135 -52 49 -114 99 -137 111 -242 123 -589 11 -902 -291 -135
-130 -224 -245 -287 -375 -54 -110 -80 -200 -80 -285 -2 -277 -206 -528 -475
-585 -203 -43 -395 15 -545 164 -190 188 -233 466 -108 704 42 81 164 203 245
245 77 41 198 72 277 72 326 1 780 374 949 780 74 178 76 355 5 494 -11 21
-59 80 -108 132 -110 117 -135 164 -135 259 0 131 3 136 324 458 l286 287 847
-847 848 -848 -282 -282z"/>
<path d="M828 1029 c-43 -22 -78 -81 -78 -129 0 -76 74 -150 150 -150 17 0 49
9 70 20 45 23 80 80 80 130 0 50 -35 107 -80 130 -49 25 -94 25 -142 -1z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

@@ -1,6 +1,6 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 4.40039H4.11111H13" stroke="#BBC3CD" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.77799 4.4V3.2C5.77799 2.88174 5.89506 2.57652 6.10343 2.35147C6.31181 2.12643 6.59442 2 6.88911 2H9.11133C9.40601 2 9.68863 2.12643 9.897 2.35147C10.1054 2.57652 10.2224 2.88174 10.2224 3.2V4.4M11.8891 4.4V12.8C11.8891 13.1183 11.772 13.4235 11.5637 13.6485C11.3553 13.8736 11.0727 14 10.778 14H5.22244C4.92775 14 4.64514 13.8736 4.43676 13.6485C4.22839 13.4235 4.11133 13.1183 4.11133 12.8V4.4H11.8891Z" stroke="#BBC3CD" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.88867 7.40039V11.0004" stroke="#BBC3CD" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.11133 7.40039V11.0004" stroke="#BBC3CD" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3 4.3999H4.11111H13" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.77775 4.4V3.2C5.77775 2.88174 5.89481 2.57652 6.10319 2.35147C6.31156 2.12643 6.59418 2 6.88886 2H9.11108C9.40577 2 9.68838 2.12643 9.89676 2.35147C10.1051 2.57652 10.2222 2.88174 10.2222 3.2V4.4M11.8889 4.4V12.8C11.8889 13.1183 11.7718 13.4235 11.5634 13.6485C11.3551 13.8736 11.0724 14 10.7778 14H5.2222C4.92751 14 4.64489 13.8736 4.43652 13.6485C4.22815 13.4235 4.11108 13.1183 4.11108 12.8V4.4H11.8889Z" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.88892 7.3999V10.9999" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.11108 7.3999V10.9999" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 886 B

After

Width:  |  Height:  |  Size: 886 B

@@ -1,4 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.974 6.33301H7.35865C6.7922 6.33301 6.33301 6.7922 6.33301 7.35865V11.974C6.33301 12.5405 6.7922 12.9997 7.35865 12.9997H11.974C12.5405 12.9997 12.9997 12.5405 12.9997 11.974V7.35865C12.9997 6.7922 12.5405 6.33301 11.974 6.33301Z" stroke="#BBC3CD" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4.53846 9.66667H4.02564C3.75362 9.66667 3.49275 9.55861 3.3004 9.36626C3.10806 9.17392 3 8.91304 3 8.64103V4.02564C3 3.75362 3.10806 3.49275 3.3004 3.3004C3.49275 3.10806 3.75362 3 4.02564 3H8.64103C8.91304 3 9.17392 3.10806 9.36626 3.3004C9.55861 3.49275 9.66667 3.75362 9.66667 4.02564V4.53846" stroke="#BBC3CD" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 785 B

@@ -1,4 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 8.91521C7.21574 9.22582 7.49099 9.48283 7.80707 9.66881C8.12315 9.85479 8.47268 9.96538 8.83194 9.99309C9.1912 10.0208 9.5518 9.96497 9.88926 9.8294C10.2267 9.69383 10.5332 9.48169 10.7878 9.20736L12.2949 7.58431C12.7525 7.07413 13.0056 6.39083 12.9999 5.68156C12.9942 4.9723 12.73 4.29384 12.2643 3.7923C11.7986 3.29075 11.1686 3.00627 10.51 3.0001C9.85142 2.99394 9.21693 3.26659 8.7432 3.75935L7.87913 4.68448" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 7.08479C8.78426 6.77418 8.50901 6.51717 8.19293 6.33119C7.87685 6.14521 7.52732 6.03462 7.16806 6.00691C6.8088 5.9792 6.4482 6.03503 6.11074 6.1706C5.77327 6.30617 5.46683 6.51831 5.21218 6.79264L3.7051 8.41569C3.24755 8.92587 2.99437 9.60918 3.00009 10.3184C3.00582 11.0277 3.26998 11.7062 3.73569 12.2077C4.2014 12.7092 4.8314 12.9937 5.48999 12.9999C6.14858 13.0061 6.78307 12.7334 7.2568 12.2407L8.11584 11.3155" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

@@ -1,8 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="9" y="3" width="2" height="2" rx="0.5" fill="#333333"/>
<rect x="5" y="3" width="2" height="2" rx="0.5" fill="#333333"/>
<rect x="9" y="7" width="2" height="2" rx="0.5" fill="#333333"/>
<rect x="5" y="7" width="2" height="2" rx="0.5" fill="#333333"/>
<rect x="9" y="11" width="2" height="2" rx="0.5" fill="#333333"/>
<rect x="5" y="11" width="2" height="2" rx="0.5" fill="#333333"/>
</svg>

Before

Width:  |  Height:  |  Size: 495 B

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="#000000" width="800px" height="800px" viewBox="-0.02 0 122.88 122.88" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="enable-background:new 0 0 122.84 122.88" xml:space="preserve">
<g>
<path d="M81.54,4.71c0-2.62,2.58-4.71,5.77-4.71c3.2,0,5.77,2.13,5.77,4.71V25.4c0,2.62-2.58,4.71-5.77,4.71 c-3.2,0-5.77-2.13-5.77-4.71V4.71L81.54,4.71z M20.4,89.87l7.55-0.47c0.16,1.22,0.5,2.16,1,2.79c0.82,1.04,1.99,1.56,3.51,1.56 c1.13,0,2.01-0.26,2.62-0.8C35.69,92.41,36,91.8,36,91.1c0-0.66-0.29-1.26-0.87-1.79c-0.58-0.53-1.93-1.02-4.06-1.49 c-3.49-0.78-5.96-1.82-7.45-3.12c-1.5-1.29-2.25-2.95-2.25-4.96c0-1.32,0.38-2.56,1.15-3.74c0.77-1.18,1.92-2.1,3.46-2.77 c1.54-0.67,3.65-1.01,6.32-1.01c3.29,0,5.79,0.61,7.52,1.84c1.72,1.22,2.75,3.17,3.08,5.84l-7.47,0.44 c-0.2-1.17-0.62-2.02-1.25-2.55c-0.64-0.53-1.52-0.8-2.64-0.8c-0.92,0-1.62,0.2-2.09,0.59c-0.47,0.39-0.7,0.87-0.7,1.43 c0,0.41,0.19,0.77,0.57,1.1c0.37,0.34,1.25,0.65,2.65,0.95c3.47,0.75,5.96,1.51,7.46,2.28c1.5,0.77,2.6,1.71,3.28,2.85 c0.68,1.13,1.02,2.4,1.02,3.81c0,1.65-0.46,3.17-1.37,4.56c-0.92,1.39-2.19,2.45-3.83,3.17c-1.64,0.72-3.7,1.08-6.19,1.08 c-4.37,0-7.4-0.84-9.09-2.53C21.58,94.6,20.62,92.46,20.4,89.87L20.4,89.87z M64.53,72.65h7.94v15.33c0,1.52-0.24,2.95-0.71,4.3 c-0.47,1.35-1.22,2.53-2.23,3.55c-1.01,1.01-2.07,1.72-3.18,2.13c-1.54,0.57-3.4,0.86-5.56,0.86c-1.25,0-2.62-0.09-4.1-0.26 c-1.48-0.17-2.72-0.52-3.71-1.04c-0.99-0.52-1.9-1.26-2.72-2.22c-0.83-0.96-1.39-1.95-1.69-2.96c-0.49-1.64-0.74-3.08-0.74-4.35 V72.65h7.94v15.69c0,1.4,0.39,2.5,1.16,3.28c0.78,0.79,1.86,1.19,3.23,1.19c1.36,0,2.43-0.39,3.21-1.17 c0.77-0.77,1.16-1.87,1.16-3.3V72.65L64.53,72.65z M77.85,72.65h7.42l9.65,14.21V72.65h7.51v25.73h-7.51l-9.59-14.13v14.13h-7.47 V72.65L77.85,72.65z M29.53,4.71c0-2.62,2.58-4.71,5.77-4.71c3.2,0,5.77,2.13,5.77,4.71V25.4c0,2.62-2.58,4.71-5.77,4.71 c-3.2,0-5.77-2.13-5.77-4.71V4.71L29.53,4.71z M7.56,44.09h107.62V22.66c0-0.8-0.31-1.55-0.84-2.04c-0.53-0.53-1.24-0.84-2.04-0.84 h-9.31c-1.78,0-3.2-2.63-3.2-4.41c0-1.78,1.42-3.2,3.2-3.2h10.53c2.58,0,4.88,1.07,6.57,2.75c1.69,1.69,2.75,4.04,2.75,6.57v92.06 c0,2.58-1.07,4.88-2.75,6.57c-1.69,1.69-4.04,2.75-6.57,2.75H9.33c-2.58,0-4.88-1.07-6.57-2.75C1.07,118.44,0,116.08,0,113.55 V21.49c0-2.58,1.07-4.88,2.75-6.57c1.69-1.69,4.04-2.75,6.57-2.75h11.28c1.78,0,3.2,1.42,3.2,3.2s-1.42,4.41-3.2,4.41H10.54 c-0.8,0-1.55,0.31-2.09,0.84c-0.53,0.53-0.84,1.24-0.84,2.09v21.43L7.56,44.09L7.56,44.09z M115.19,52.9H7.56v59.4 c0,0.8,0.31,1.55,0.84,2.09c0.53,0.53,1.24,0.84,2.09,0.84h101.76c0.8,0,1.55-0.31,2.09-0.84c0.53-0.53,0.84-1.24,0.84-2.09V52.9 L115.19,52.9z M50.36,19.73c-1.78,0-3.2-2.63-3.2-4.41c0-1.78,1.42-3.2,3.2-3.2h21.49c1.78,0,3.2,1.42,3.2,3.2 c0,1.78-1.42,4.41-3.2,4.41H50.36L50.36,19.73z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 6C3 3.79086 4.79086 2 7 2H17C19.2091 2 21 3.79086 21 6C21 8.20914 19.2091 10 17 10H7C4.79086 10 3 8.20914 3 6Z" stroke="#000000" stroke-width="2"/>
<path d="M3 16C3 14.8954 3.89543 14 5 14H8C9.10457 14 10 14.8954 10 16V19C10 20.1046 9.10457 21 8 21H5C3.89543 21 3 20.1046 3 19V16Z" stroke="#000000" stroke-width="2"/>
<path d="M14 17.5C14 15.567 15.567 14 17.5 14C19.433 14 21 15.567 21 17.5C21 19.433 19.433 21 17.5 21C15.567 21 14 19.433 14 17.5Z" stroke="#000000" stroke-width="2"/>
</svg>

Before

Width:  |  Height:  |  Size: 725 B

@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="127px" height="96px" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd" xmlns:xlink="http://www.w3.org/1999/xlink">
<g><path style="opacity:0.982" fill="#5864f2" d="M 40.5,-0.5 C 42.5,-0.5 44.5,-0.5 46.5,-0.5C 46.7534,3.03777 48.5868,5.03777 52,5.5C 59.3333,4.16667 66.6667,4.16667 74,5.5C 77.4132,5.03777 79.2466,3.03777 79.5,-0.5C 81.8333,-0.5 84.1667,-0.5 86.5,-0.5C 93.6784,1.84095 100.845,4.50762 108,7.5C 117.556,21.9281 123.723,37.5947 126.5,54.5C 126.5,63.1667 126.5,71.8333 126.5,80.5C 117.234,86.2996 107.567,91.2996 97.5,95.5C 95.8333,95.5 94.1667,95.5 92.5,95.5C 90.2583,92.0205 88.2583,88.3538 86.5,84.5C 89.9621,82.9349 93.2954,81.1016 96.5,79C 95.8333,78.8333 95.1667,78.6667 94.5,78.5C 80.6757,84.0674 66.3424,85.9007 51.5,84C 44.0284,82.4993 36.6951,80.8327 29.5,79C 32.7046,81.1016 36.0379,82.9349 39.5,84.5C 37.7417,88.3538 35.7417,92.0205 33.5,95.5C 31.8333,95.5 30.1667,95.5 28.5,95.5C 18.4329,91.2996 8.76625,86.2996 -0.5,80.5C -0.5,71.8333 -0.5,63.1667 -0.5,54.5C 2.27734,37.5947 8.44401,21.9281 18,7.5C 25.419,4.30685 32.919,1.64019 40.5,-0.5 Z M 38.5,40.5 C 51.8373,41.4956 55.6706,48.1623 50,60.5C 42.3899,66.5776 36.3899,65.2442 32,56.5C 30.062,49.3891 32.2287,44.0558 38.5,40.5 Z M 80.5,40.5 C 93.8373,41.4956 97.6706,48.1623 92,60.5C 84.3899,66.5776 78.3899,65.2442 74,56.5C 72.062,49.3891 74.2287,44.0558 80.5,40.5 Z"/></g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

@@ -1 +0,0 @@
<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#fff"/></svg>

Before

Width:  |  Height:  |  Size: 960 B

@@ -1 +0,0 @@
<svg xmlns:xlink="http://www.w3.org/1999/xlink" data-e2e="" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg" width="1" height="1" ><path fill-rule="evenodd" clip-rule="evenodd" d="M43 24.4313C43 23.084 42.8767 21.7885 42.6475 20.5449H24.3877V27.8945H34.8219C34.3724 30.2695 33.0065 32.2818 30.9532 33.6291V38.3964H37.2189C40.885 35.0886 43 30.2177 43 24.4313Z" fill="#4285F4"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M24.3872 43.001C29.6219 43.001 34.0107 41.2996 37.2184 38.3978L30.9527 33.6305C29.2165 34.7705 26.9958 35.4441 24.3872 35.4441C19.3375 35.4441 15.0633 32.1018 13.5388 27.6108H7.06152V32.5337C10.2517 38.7433 16.8082 43.001 24.3872 43.001Z" fill="#34A853"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M13.5395 27.6094C13.1516 26.4695 12.9313 25.2517 12.9313 23.9994C12.9313 22.7472 13.1516 21.5295 13.5395 20.3894V15.4668H7.06217C5.74911 18.0318 5 20.9336 5 23.9994C5 27.0654 5.74911 29.9673 7.06217 32.5323L13.5395 27.6094Z" fill="#FBBC04"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M24.3872 12.5568C27.2336 12.5568 29.7894 13.5155 31.7987 15.3982L37.3595 9.94866C34.0018 6.88281 29.6131 5 24.3872 5C16.8082 5 10.2517 9.25777 7.06152 15.4674L13.5388 20.39C15.0633 15.8991 19.3375 12.5568 24.3872 12.5568Z" fill="#EA4335"></path></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

@@ -1,254 +0,0 @@
{
"document": {
"type": "editor",
"children": [
{ "type": "cover" },
{
"type": "text",
"attributes": { "subtype": "heading", "heading": "h1" },
"delta": [{ "insert": "Welcome to AppFlowy!" }]
},
{
"type": "text",
"attributes": { "subtype": "heading", "heading": "h2" },
"delta": [{ "insert": "Here are the basics" }]
},
{
"type": "text",
"attributes": { "subtype": "checkbox", "checkbox": null },
"delta": [{ "insert": "Click anywhere and just start typing." }]
},
{
"type": "text",
"attributes": { "subtype": "checkbox", "checkbox": false },
"delta": [
{
"insert": "Highlight ",
"attributes": { "backgroundColor": "0x4dffeb3b" }
},
{ "insert": "any text, and use the editing menu to " },
{ "insert": "style", "attributes": { "italic": true } },
{ "insert": " " },
{ "insert": "your", "attributes": { "bold": true } },
{ "insert": " " },
{ "insert": "writing", "attributes": { "underline": true } },
{ "insert": " " },
{ "insert": "however", "attributes": { "code": true } },
{ "insert": " you " },
{ "insert": "like.", "attributes": { "strikethrough": true } }
]
},
{
"type": "text",
"attributes": { "subtype": "checkbox", "checkbox": null },
"delta": [
{ "insert": "As soon as you type " },
{
"insert": "/",
"attributes": { "code": true, "color": "0xff00b5ff" }
},
{ "insert": " a menu will pop up. Select " },
{
"insert": "different types",
"attributes": { "backgroundColor": "0x4d9c27b0" }
},
{ "insert": " of content blocks you can add." }
]
},
{
"type": "text",
"attributes": { "subtype": "checkbox", "checkbox": null },
"delta": [
{ "insert": "Type " },
{ "insert": "/", "attributes": { "code": true } },
{ "insert": " followed by " },
{ "insert": "/bullet", "attributes": { "code": true } },
{ "insert": " or " },
{ "insert": "/num", "attributes": { "code": true } },
{ "insert": " to create a list.", "attributes": { "code": false } }
]
},
{
"type": "text",
"attributes": { "subtype": "checkbox", "checkbox": true },
"delta": [
{ "insert": "Click " },
{ "insert": "+ New Page ", "attributes": { "code": true } },
{
"insert": "button at the bottom of your sidebar to add a new page."
}
]
},
{
"type": "text",
"attributes": { "subtype": "checkbox", "checkbox": null },
"delta": [
{ "insert": "Click " },
{ "insert": "+", "attributes": { "code": true } },
{ "insert": " next to any page title in the sidebar to " },
{ "insert": "quickly", "attributes": { "color": "0xff8427e0" } },
{ "insert": " add a new subpage, " },
{ "insert": "Document", "attributes": { "code": true } },
{ "insert": ", ", "attributes": { "code": false } },
{ "insert": "Grid", "attributes": { "code": true } },
{ "insert": ", or ", "attributes": { "code": false } },
{ "insert": "Kanban Board", "attributes": { "code": true } },
{ "insert": ".", "attributes": { "code": false } }
]
},
{ "type": "text", "delta": [] },
{ "type": "divider" },
{ "type": "text", "attributes": { "checkbox": null }, "delta": [] },
{
"type": "text",
"attributes": {
"subtype": "heading",
"checkbox": null,
"heading": "h2"
},
"delta": [{ "insert": "Keyboard shortcuts, markdown, and code block" }]
},
{
"type": "text",
"attributes": {
"subtype": "number-list",
"number": 1,
"heading": null
},
"delta": [
{ "insert": "Keyboard shortcuts " },
{
"insert": "guide",
"attributes": {
"href": "https://appflowy.gitbook.io/docs/essential-documentation/shortcuts"
}
},
{ "retain": 1, "attributes": { "strikethrough": true } }
]
},
{
"type": "text",
"attributes": {
"subtype": "number-list",
"number": 2,
"heading": null
},
"delta": [
{ "insert": "Markdown " },
{
"insert": "reference",
"attributes": {
"href": "https://appflowy.gitbook.io/docs/essential-documentation/markdown"
}
},
{ "retain": 1, "attributes": { "strikethrough": true } }
]
},
{
"type": "text",
"attributes": { "number": 3, "subtype": "number-list" },
"delta": [
{ "insert": "Type " },
{ "insert": "/code", "attributes": { "code": true } },
{
"insert": " to insert a code block",
"attributes": { "code": false }
}
]
},
{
"type": "text",
"attributes": {
"subtype": "code_block",
"number": 3,
"heading": null,
"number-list": null,
"theme": "vs",
"language": "rust"
},
"delta": [
{
"insert": "// This is the main function.\nfn main() {\n // Print text to the console.\n println!(\"Hello World!\");\n}"
},
{ "retain": 1, "attributes": { "strikethrough": true } }
]
},
{ "type": "text", "attributes": { "checkbox": null }, "delta": [] },
{
"type": "text",
"attributes": {
"subtype": "heading",
"checkbox": null,
"heading": "h2"
},
"delta": [{ "insert": "Have a question❓" }]
},
{
"type": "text",
"attributes": { "subtype": "quote" },
"delta": [
{ "insert": "Click " },
{ "insert": "?", "attributes": { "code": true } },
{ "insert": " at the bottom right for help and support." }
]
},
{ "type": "text", "delta": [] },
{
"type": "callout",
"children": [
{ "type": "text", "delta": [] },
{
"type": "text",
"attributes": { "subtype": "heading", "heading": "h2" },
"delta": [{ "insert": "Like AppFlowy? Follow us:" }]
},
{
"type": "text",
"attributes": { "subtype": "bulleted-list" },
"delta": [
{
"insert": "GitHub",
"attributes": {
"href": "https://github.com/AppFlowy-IO/AppFlowy"
}
}
]
},
{
"type": "text",
"attributes": { "subtype": "bulleted-list" },
"delta": [
{
"insert": "Twitter",
"attributes": { "href": "https://twitter.com/appflowy" }
},
{ "insert": ": @appflowy" }
]
},
{
"type": "text",
"attributes": { "subtype": "bulleted-list" },
"delta": [
{
"insert": "Newsletter",
"attributes": { "href": "https://blog-appflowy.ghost.io/" }
}
]
}
],
"attributes": { "emoji": "😀" }
},
{ "type": "text", "delta": [] },
{
"type": "text",
"attributes": { "subtype": null, "heading": null },
"delta": []
},
{
"type": "text",
"attributes": { "subtype": null, "heading": null },
"delta": []
}
]
}
}
@@ -1,254 +0,0 @@
{
"document": {
"type": "editor",
"children": [
{ "type": "cover" },
{
"type": "text",
"attributes": { "subtype": "heading", "heading": "h1" },
"delta": [{ "insert": "Welcome to AppFlowy!" }]
},
{
"type": "text",
"attributes": { "subtype": "heading", "heading": "h2" },
"delta": [{ "insert": "Here are the basics" }]
},
{
"type": "text",
"attributes": { "subtype": "checkbox", "checkbox": null },
"delta": [{ "insert": "Click anywhere and just start typing." }]
},
{
"type": "text",
"attributes": { "subtype": "checkbox", "checkbox": false },
"delta": [
{
"insert": "Highlight ",
"attributes": { "backgroundColor": "0x4dffeb3b" }
},
{ "insert": "any text, and use the editing menu to " },
{ "insert": "style", "attributes": { "italic": true } },
{ "insert": " " },
{ "insert": "your", "attributes": { "bold": true } },
{ "insert": " " },
{ "insert": "writing", "attributes": { "underline": true } },
{ "insert": " " },
{ "insert": "however", "attributes": { "code": true } },
{ "insert": " you " },
{ "insert": "like.", "attributes": { "strikethrough": true } }
]
},
{
"type": "text",
"attributes": { "subtype": "checkbox", "checkbox": null },
"delta": [
{ "insert": "As soon as you type " },
{
"insert": "/",
"attributes": { "code": true, "color": "0xff00b5ff" }
},
{ "insert": " a menu will pop up. Select " },
{
"insert": "different types",
"attributes": { "backgroundColor": "0x4d9c27b0" }
},
{ "insert": " of content blocks you can add." }
]
},
{
"type": "text",
"attributes": { "subtype": "checkbox", "checkbox": null },
"delta": [
{ "insert": "Type " },
{ "insert": "/", "attributes": { "code": true } },
{ "insert": " followed by " },
{ "insert": "/bullet", "attributes": { "code": true } },
{ "insert": " or " },
{ "insert": "/num", "attributes": { "code": true } },
{ "insert": " to create a list.", "attributes": { "code": false } }
]
},
{
"type": "text",
"attributes": { "subtype": "checkbox", "checkbox": true },
"delta": [
{ "insert": "Click " },
{ "insert": "+ New Page ", "attributes": { "code": true } },
{
"insert": "button at the bottom of your sidebar to add a new page."
}
]
},
{
"type": "text",
"attributes": { "subtype": "checkbox", "checkbox": null },
"delta": [
{ "insert": "Click " },
{ "insert": "+", "attributes": { "code": true } },
{ "insert": " next to any page title in the sidebar to " },
{ "insert": "quickly", "attributes": { "color": "0xff8427e0" } },
{ "insert": " add a new subpage, " },
{ "insert": "Document", "attributes": { "code": true } },
{ "insert": ", ", "attributes": { "code": false } },
{ "insert": "Grid", "attributes": { "code": true } },
{ "insert": ", or ", "attributes": { "code": false } },
{ "insert": "Kanban Board", "attributes": { "code": true } },
{ "insert": ".", "attributes": { "code": false } }
]
},
{ "type": "text", "delta": [] },
{ "type": "divider" },
{ "type": "text", "attributes": { "checkbox": null }, "delta": [] },
{
"type": "text",
"attributes": {
"subtype": "heading",
"checkbox": null,
"heading": "h2"
},
"delta": [{ "insert": "Keyboard shortcuts, markdown, and code block" }]
},
{
"type": "text",
"attributes": {
"subtype": "number-list",
"number": 1,
"heading": null
},
"delta": [
{ "insert": "Keyboard shortcuts " },
{
"insert": "guide",
"attributes": {
"href": "https://appflowy.gitbook.io/docs/essential-documentation/shortcuts"
}
},
{ "retain": 1, "attributes": { "strikethrough": true } }
]
},
{
"type": "text",
"attributes": {
"subtype": "number-list",
"number": 2,
"heading": null
},
"delta": [
{ "insert": "Markdown " },
{
"insert": "reference",
"attributes": {
"href": "https://appflowy.gitbook.io/docs/essential-documentation/markdown"
}
},
{ "retain": 1, "attributes": { "strikethrough": true } }
]
},
{
"type": "text",
"attributes": { "number": 3, "subtype": "number-list" },
"delta": [
{ "insert": "Type " },
{ "insert": "/code", "attributes": { "code": true } },
{
"insert": " to insert a code block",
"attributes": { "code": false }
}
]
},
{
"type": "text",
"attributes": {
"subtype": "code_block",
"number": 3,
"heading": null,
"number-list": null,
"theme": "vs",
"language": "rust"
},
"delta": [
{
"insert": "// This is the main function.\nfn main() {\n // Print text to the console.\n println!(\"Hello World!\");\n}"
},
{ "retain": 1, "attributes": { "strikethrough": true } }
]
},
{ "type": "text", "attributes": { "checkbox": null }, "delta": [] },
{
"type": "text",
"attributes": {
"subtype": "heading",
"checkbox": null,
"heading": "h2"
},
"delta": [{ "insert": "Have a question❓" }]
},
{
"type": "text",
"attributes": { "subtype": "quote" },
"delta": [
{ "insert": "Click " },
{ "insert": "?", "attributes": { "code": true } },
{ "insert": " at the bottom right for help and support." }
]
},
{ "type": "text", "delta": [] },
{
"type": "callout",
"children": [
{ "type": "text", "delta": [] },
{
"type": "text",
"attributes": { "subtype": "heading", "heading": "h2" },
"delta": [{ "insert": "Like AppFlowy? Follow us:" }]
},
{
"type": "text",
"attributes": { "subtype": "bulleted-list" },
"delta": [
{
"insert": "GitHub",
"attributes": {
"href": "https://github.com/AppFlowy-IO/AppFlowy"
}
}
]
},
{
"type": "text",
"attributes": { "subtype": "bulleted-list" },
"delta": [
{
"insert": "Twitter",
"attributes": { "href": "https://twitter.com/appflowy" }
},
{ "insert": ": @appflowy" }
]
},
{
"type": "text",
"attributes": { "subtype": "bulleted-list" },
"delta": [
{
"insert": "Newsletter",
"attributes": { "href": "https://blog-appflowy.ghost.io/" }
}
]
}
],
"attributes": { "emoji": "😀" }
},
{ "type": "text", "delta": [] },
{
"type": "text",
"attributes": { "subtype": null, "heading": null },
"delta": []
},
{
"type": "text",
"attributes": { "subtype": null, "heading": null },
"delta": []
}
]
}
}
@@ -6,8 +6,6 @@
"subscribeNewsletterText": "Subscribe to Newsletter",
"letsGoButtonText": "Quick Start",
"title": "Title",
"youCanAlso": "You can also",
"and": "and",
"signUp": {
"buttonText": "Sign Up",
"title": "Sign Up to @:appName",
@@ -23,7 +21,6 @@
"signIn": {
"loginTitle": "Login to @:appName",
"loginButtonText": "Login",
"loginAsGuestButtonText": "Get Started",
"buttonText": "Sign In",
"forgotPassword": "Forgot Password?",
"emailHint": "Email",
@@ -48,8 +45,7 @@
"medium": "medium",
"large": "large",
"fontSize": "Font Size",
"import": "Import",
"moreOptions": "More options"
"import": "Import"
},
"disclosureAction": {
"rename": "Rename",
@@ -112,7 +108,6 @@
"openAsPage": "Open as a Page",
"addNewRow": "Add a new row",
"openMenu": "Click to open menu",
"dragRow": "Long press to reorder the row",
"viewDataBase": "View database",
"referencePage": "This {name} is referenced"
},
@@ -187,20 +182,15 @@
"theme": "Theme"
},
"files": {
"copy": "Copy",
"defaultLocation": "Read files and data storage location",
"exportData": "Export your data",
"defaultLocation": "Where your data is stored now",
"doubleTapToCopy": "Double tap to copy the path",
"restoreLocation": "Restore to AppFlowy default path",
"customizeLocation": "Open another folder",
"restartApp": "Please restart app for the changes to take effect.",
"exportDatabase": "Export database",
"selectFiles": "Select the files that need to be export",
"selectAll": "Select all",
"deselectAll": "Deselect all",
"createNewFolder": "Create a new folder",
"createNewFolderDesc": "Tell us where you want to store your data",
"defineWhereYourDataIsStored": "Define where your data is stored",
"open": "Open",
"openFolder": "Open an existing folder",
"openFolderDesc": "Read and write it to your existing AppFlowy folder",
@@ -209,17 +199,9 @@
"locationDesc": "Pick a name for your AppFlowy data folder",
"browser": "Browse",
"create": "Create",
"set": "Set",
"folderPath": "Path to store your folder",
"locationCannotBeEmpty": "Path cannot be empty",
"pathCopiedSnackbar": "File storage path copied to clipboard!",
"changeLocationTooltips": "Change the data directory",
"change": "Change",
"openLocationTooltips": "Open another data directory",
"recoverLocationTooltips": "Reset to AppFlowy's default data directory",
"exportFileSuccess": "Export file successfully!",
"exportFileFail": "Export file failed!",
"export": "Export"
"pathCopiedSnackbar": "File storage path copied to clipboard!"
},
"user": {
"name": "Name",
@@ -239,8 +221,7 @@
"deleteFilter": "Delete filter",
"filterBy": "Filter by...",
"typeAValue": "Type a value...",
"layout": "Layout",
"databaseLayout": "Layout"
"layout": "Layout"
},
"textFilter": {
"contains": "Contains",
@@ -291,8 +272,6 @@
"textFieldName": "Text",
"checkboxFieldName": "Checkbox",
"dateFieldName": "Date",
"updatedAtFieldName": "Last modified time",
"createdAtFieldName": "Created time",
"numberFieldName": "Numbers",
"singleSelectFieldName": "Select",
"multiSelectFieldName": "Multiselect",
@@ -368,30 +347,23 @@
"grid": {
"selectAGridToLinkTo": "Select a Grid to link to",
"createANewGrid": "Create a new Grid"
},
"calendar": {
"selectACalendarToLinkTo": "Select a Calendar to link to",
"createANewCalendar": "Create a new Calendar"
}
},
"plugins": {
"referencedBoard": "Referenced Board",
"referencedGrid": "Referenced Grid",
"referencedCalendar": "Referenced Calendar",
"autoGeneratorMenuItemName": "OpenAI Writer",
"autoGeneratorTitleName": "OpenAI: Ask AI to write anything...",
"autoGeneratorLearnMore": "Learn more",
"autoGeneratorGenerate": "Generate",
"autoGeneratorHintText": "Ask OpenAI ...",
"autoGeneratorCantGetOpenAIKey": "Can't get OpenAI key",
"autoGeneratorRewrite": "Rewrite",
"smartEdit": "AI Assistants",
"openAI": "OpenAI",
"smartEditFixSpelling": "Fix spelling",
"warning": "⚠️ AI responses can be inaccurate or misleading.",
"smartEditSummarize": "Summarize",
"smartEditImproveWriting": "Improve writing",
"smartEditMakeLonger": "Make longer",
"smartEditImproveWriting": "Improve Writing",
"smartEditCouldNotFetchResult": "Could not fetch result from OpenAI",
"smartEditCouldNotFetchKey": "Could not fetch OpenAI key",
"smartEditDisabled": "Connect OpenAI in Settings",
@@ -423,21 +395,6 @@
"mathEquation": {
"addMathEquation": "Add Math Equation",
"editMathEquation": "Edit Math Equation"
},
"optionAction": {
"click": "Click",
"toOpenMenu": " to open menu",
"delete": "Delete",
"duplicate": "Duplicate",
"turnInto": "Turn into",
"moveUp": "Move up",
"moveDown": "Move down",
"color": "Color",
"align": "Align",
"left": "Left",
"center": "Center",
"right": "Right",
"defaultColor": "Default"
}
}
},
@@ -463,10 +420,7 @@
"firstDayOfWeek": "Start week on",
"layoutDateField": "Layout calendar by",
"noDateTitle": "No Date",
"noDateHint": "Unscheduled events will show up here",
"clickToAdd": "Click to add to the calendar",
"name": "Calendar layout"
},
"referencedCalendarPrefix": "View of"
"emptyNoDate": "No unscheduled events"
}
}
}
@@ -35,7 +35,9 @@ void main() {
setUpAll(() async => await service.setUpAll());
setUp(() async => await service.setUp());
testWidgets('open the board with data structure in v0.2.0', (tester) async {
testWidgets(
'integration test unzips the proper workspace and loads it correctly.',
(tester) async {
await tester.initializeAppFlowy();
expect(find.byType(AppFlowyBoard), findsOneWidget);
});
@@ -1,35 +1,54 @@
import 'package:flowy_infra_ui/widget/rounded_button.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'util/util.dart';
/// Integration tests for an empty board. The [TestWorkspaceService] will load
/// a workspace from an empty board `assets/test/workspaces/board.zip` for all
/// tests.
///
/// To create another integration test with a preconfigured workspace.
/// Use the following steps.
/// 1. Create a new workspace from the AppFlowy launch screen.
/// 2. Modify the workspace until it is suitable as the starting point for
/// the integration test you need to land.
/// 3. Use a zip utility program to zip the workspace folder that you created.
/// 4. Add the zip file under `assets/test/workspaces/`
/// 5. Add a new enumeration to [TestWorkspace] in `integration_test/utils/data.dart`.
/// For example, if you added a workspace called `empty_calendar.zip`,
/// then [TestWorkspace] should have the following value:
/// ```dart
/// enum TestWorkspace {
/// board('board'),
/// empty_calendar('empty_calendar');
///
/// /* code */
/// }
/// ```
/// 6. Double check that the .zip file that you added is included as an asset in
/// the pubspec.yaml file under appflowy_flutter.
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
const service = TestWorkspaceService(TestWorkspace.coverImage);
group('cover image', () {
const location = 'cover_image';
setUp(() async {
await TestFolder.cleanTestLocation(location);
await TestFolder.setTestLocation(location);
});
tearDown(() async {
await TestFolder.cleanTestLocation(location);
});
tearDownAll(() async {
await TestFolder.cleanTestLocation(null);
});
setUpAll(() async => await service.setUpAll());
setUp(() async => await service.setUp());
testWidgets(
'hovering on cover image will display change and delete cover image buttons',
(tester) async {
await tester.initializeAppFlowy();
expect(find.byType(Image), findsOneWidget);
await tester.tapGoButton();
await tester.hoverOnCoverPluginAddButton();
await tester.expectToSeePluginAddCoverAndIconButton();
final TestPointer pointer = TestPointer(1, PointerDeviceKind.mouse);
final imageFinder = find.byType(Image);
Offset offset = tester.getCenter(imageFinder);
pointer.hover(offset);
expect(find.byType(RoundedTextButton), findsOneWidget);
});
});
}
@@ -1,90 +0,0 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'util/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('document', () {
const location = 'appflowy';
setUp(() async {
await TestFolder.cleanTestLocation(location);
await TestFolder.setTestLocation(location);
});
tearDown(() async {
await TestFolder.cleanTestLocation(location);
});
tearDownAll(() async {
await TestFolder.cleanTestLocation(null);
});
testWidgets('create a new document when launching app in first time',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
// create a new document
await tester.tapAddButton();
await tester.tapCreateDocumentButton();
await tester.pumpAndSettle();
// expect to see a new document
tester.expectToSeePageName(
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
);
// and with one paragraph block
expect(find.byType(TextBlockComponentWidget), findsOneWidget);
});
testWidgets('delete the readme page and restore it', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
// delete the readme page
await tester.hoverOnPageName(readme);
await tester.tapDeletePageButton();
// the banner should show up and the readme page should be gone
tester.expectToSeeDocumentBanner();
tester.expectNotToSeePageName(readme);
// restore the readme page
await tester.tapRestoreButton();
// the banner should be gone and the readme page should be back
tester.expectNotToSeeDocumentBanner();
tester.expectToSeePageName(readme);
});
testWidgets('delete the readme page and delete it permanently',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
// delete the readme page
await tester.hoverOnPageName(readme);
await tester.tapDeletePageButton();
// the banner should show up and the readme page should be gone
tester.expectToSeeDocumentBanner();
tester.expectNotToSeePageName(readme);
// delete the page permanently
await tester.tapDeletePermanentlyButton();
// the banner should be gone and the readme page should be gone
tester.expectNotToSeeDocumentBanner();
tester.expectNotToSeePageName(readme);
});
});
}
@@ -1,4 +1,4 @@
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/built_in_page_widget.dart';
import 'package:appflowy/plugins/document/presentation/plugins/base/built_in_page_widget.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -1,4 +1,4 @@
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/openai_client.dart';
import 'package:appflowy/plugins/document/presentation/plugins/openai/service/openai_client.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -1,8 +1,9 @@
import 'package:integration_test/integration_test.dart';
import 'board_test.dart' as board_test;
import 'switch_folder_test.dart' as switch_folder_test;
import 'document_test.dart' as document_test;
import 'cover_image_test.dart' as cover_image_test;
import 'empty_document_test.dart' as empty_document_test;
import 'open_ai_smart_menu_test.dart' as smart_menu_test;
/// The main task runner for all integration tests in AppFlowy.
///
@@ -14,9 +15,7 @@ import 'cover_image_test.dart' as cover_image_test;
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
switch_folder_test.main();
cover_image_test.main();
document_test.main();
// board_test.main();
// empty_document_test.main();
// smart_menu_test.main();
board_test.main();
empty_document_test.main();
smart_menu_test.main();
}
@@ -1,75 +0,0 @@
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'util/mock/mock_file_picker.dart';
import 'util/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('share markdown in document page', () {
const location = 'markdown';
setUp(() async {
await TestFolder.cleanTestLocation(location);
await TestFolder.setTestLocation(location);
});
tearDown(() async {
await TestFolder.cleanTestLocation(location);
});
tearDownAll(() async {
await TestFolder.cleanTestLocation(null);
});
testWidgets('click the share button in document page', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
// expect to see a readme page
tester.expectToSeePageName(readme);
// mock the file picker
final path = await mockSaveFilePath(location, 'test.md');
// click the share button and select markdown
await tester.tapShareButton();
await tester.tapMarkdownButton();
// expect to see the success dialog
tester.expectToExportSuccess();
final file = File(path);
final isExist = file.existsSync();
expect(isExist, true);
final markdown = file.readAsStringSync();
expect(markdown, expectedMarkdown);
});
});
}
const expectedMarkdown = r'''
# Welcome to AppFlowy!
## Here are the basics
- [ ] Click anywhere and just start typing.
- [ ] Highlight any text, and use the editing menu to _style_ **your** <u>writing</u> `however` you ~~like.~~
- [ ] As soon as you type `/` a menu will pop up. Select different types of content blocks you can add.
- [ ] Type `/` followed by `/bullet` or `/num` to create a list.
- [x] Click `+ New Page `button at the bottom of your sidebar to add a new page.
- [ ] Click `+` next to any page title in the sidebar to quickly add a new subpage, `Document`, `Grid`, or `Kanban Board`.
---
## Keyboard shortcuts, markdown, and code block
1. Keyboard shortcuts [guide](https://appflowy.gitbook.io/docs/essential-documentation/shortcuts)
1. Markdown [reference](https://appflowy.gitbook.io/docs/essential-documentation/markdown)
1. Type `/code` to insert a code block
## Have a question❓
> Click `?` at the bottom right for help and support.
''';
@@ -1,4 +1,5 @@
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/user/presentation/folder/folder_widget.dart';
import 'package:flowy_infra_ui/style_widget/text_field.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
@@ -24,22 +25,69 @@ void main() {
await TestFolder.cleanTestLocation(null);
});
testWidgets(
'customize folder name and path when launching app in first time',
(tester) async {
const folderName = 'appflowy';
await TestFolder.cleanTestLocation(folderName);
await tester.initializeAppFlowy();
// Click create button
await tester.tapCreateButton();
// Set directory
final cfw = find.byType(CreateFolderWidget);
expect(cfw, findsOneWidget);
final state = tester.state(cfw) as CreateFolderWidgetState;
final dir = await TestFolder.testLocation(null);
state.directory = dir.path;
// input folder name
final ftf = find.byType(FlowyTextField);
expect(ftf, findsOneWidget);
await tester.enterText(ftf, 'appflowy');
// Click create button again
await tester.tapCreateButton();
await tester.expectToSeeWelcomePage();
await TestFolder.cleanTestLocation(folderName);
});
testWidgets('open a new folder when launching app in first time',
(tester) async {
const folderName = 'appflowy';
await TestFolder.cleanTestLocation(folderName);
await TestFolder.setTestLocation(folderName);
await tester.initializeAppFlowy();
// tap open button
await mockGetDirectoryPath(folderName);
await tester.tapOpenFolderButton();
await tester.wait(1000);
await tester.expectToSeeWelcomePage();
await TestFolder.cleanTestLocation(folderName);
});
testWidgets('switch to B from A, then switch to A again', (tester) async {
const String userA = 'userA';
const String userB = 'userB';
await TestFolder.cleanTestLocation(userA);
await TestFolder.cleanTestLocation(userB);
await TestFolder.setTestLocation(userA);
await tester.initializeAppFlowy();
await tester.tapGoButton();
tester.expectToSeeWelcomePage();
await tester.expectToSeeWelcomePage();
// switch to user B
{
// set user name to userA
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user);
await tester.enterUserName(userA);
@@ -51,16 +99,15 @@ void main() {
await mockGetDirectoryPath(userB);
await tester.tapCustomLocationButton();
await tester.pumpAndSettle();
tester.expectToSeeWelcomePage();
// set user name to userB
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user);
await tester.enterUserName(userB);
await tester.expectToSeeWelcomePage();
}
// switch to the userA
{
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user);
await tester.enterUserName(userB);
await tester.openSettingsPage(SettingsPage.files);
await tester.pumpAndSettle();
@@ -69,8 +116,8 @@ void main() {
await tester.tapCustomLocationButton();
await tester.pumpAndSettle();
tester.expectToSeeWelcomePage();
tester.expectToSeeUserName(userA);
await tester.expectToSeeWelcomePage();
expect(find.textContaining(userA), findsOneWidget);
}
// switch to the userB again
@@ -84,8 +131,8 @@ void main() {
await tester.tapCustomLocationButton();
await tester.pumpAndSettle();
tester.expectToSeeWelcomePage();
tester.expectToSeeUserName(userB);
await tester.expectToSeeWelcomePage();
expect(find.textContaining(userB), findsOneWidget);
}
await TestFolder.cleanTestLocation(userA);
@@ -98,7 +145,7 @@ void main() {
await tester.tapGoButton();
// home and readme document
tester.expectToSeeWelcomePage();
await tester.expectToSeeWelcomePage();
// open settings and restore the location
await tester.openSettings();
@@ -1,8 +1,8 @@
import 'dart:io';
import 'package:appflowy/core/config/kv_keys.dart';
import 'package:appflowy/main.dart' as app;
import 'package:appflowy/startup/tasks/prelude.dart';
import 'package:appflowy/workspace/application/settings/settings_location_cubit.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -21,7 +21,7 @@ class TestFolder {
static Future<void> setTestLocation(String? name) async {
final location = await testLocation(name);
SharedPreferences.setMockInitialValues({
KVKeys.pathLocation: location.path,
kSettingsLocationDefaultLocation: location.path,
});
return;
}
@@ -36,7 +36,7 @@ class TestFolder {
/// Get current using location.
static Future<String> currentLocation() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString(KVKeys.pathLocation)!;
return prefs.getString(kSettingsLocationDefaultLocation)!;
}
/// Get default location under development environment.
@@ -58,21 +58,17 @@ class TestFolder {
extension AppFlowyTestBase on WidgetTester {
Future<void> initializeAppFlowy() async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(
const MethodChannel('hotkey_manager'),
(MethodCall methodCall) async {
if (methodCall.method == 'unregisterAll') {
// do nothing
}
return;
},
);
const MethodChannel('hotkey_manager')
.setMockMethodCallHandler((MethodCall methodCall) async {
if (methodCall.method == 'unregisterAll') {
// do nothing
}
});
await app.main();
await wait(3000);
await pumpAndSettle(const Duration(seconds: 2));
return;
}
Future<void> tapButton(
@@ -91,7 +87,7 @@ extension AppFlowyTestBase on WidgetTester {
String tr, {
int milliseconds = 500,
}) async {
final button = find.text(tr, findRichText: true);
final button = find.text(tr);
await tapButton(
button,
milliseconds: milliseconds,
@@ -1,158 +0,0 @@
import 'dart:ui';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/banner.dart';
import 'package:appflowy/plugins/document/presentation/share/share_button.dart';
import 'package:appflowy/user/presentation/skip_log_in_screen.dart';
import 'package:appflowy/workspace/presentation/home/home_stack.dart';
import 'package:appflowy/workspace/presentation/home/menu/app/header/add_button.dart';
import 'package:appflowy/workspace/presentation/home/menu/app/section/item.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter_test/flutter_test.dart';
import 'base.dart';
const String readme = 'Read me';
extension CommonOperations on WidgetTester {
Future<void> tapGoButton() async {
final goButton = find.byType(GoButton);
await tapButton(goButton);
}
Future<void> tapCreateButton() async {
await tapButtonWithName(LocaleKeys.settings_files_create.tr());
}
void expectToSeeWelcomePage() {
expect(find.byType(HomeStack), findsOneWidget);
expect(find.textContaining('Read me'), findsOneWidget);
}
Future<void> tapAddButton() async {
final addButton = find.byType(AddButton);
await tapButton(addButton);
}
Future<void> tapCreateDocumentButton() async {
await tapButtonWithName(LocaleKeys.document_menuName.tr());
}
Finder findPageName(String name) {
return find.byWidgetPredicate(
(widget) => widget is ViewSectionItem && widget.view.name == name,
);
}
void expectToSeePageName(String name) {
final pageName = findPageName(name);
expect(pageName, findsOneWidget);
}
void expectNotToSeePageName(String name) {
final pageName = findPageName(name);
expect(pageName, findsNothing);
}
Future<void> hoverOnPageName(String name) async {
final pageName = find.byWidgetPredicate(
(widget) => widget is ViewSectionItem && widget.view.name == name,
);
final gesture = await createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer(location: Offset.zero);
addTearDown(gesture.removePointer);
await pump();
await gesture.moveTo(getCenter(pageName));
await pumpAndSettle();
}
Future<void> tapPageOptionButton() async {
final optionButton = find.byType(ViewDisclosureButton);
await tapButton(optionButton);
}
Future<void> tapDeletePageButton() async {
await tapPageOptionButton();
await tapButtonWithName(ViewDisclosureAction.delete.name);
}
void expectToSeeDocumentBanner() {
expect(find.byType(DocumentBanner), findsOneWidget);
}
void expectNotToSeeDocumentBanner() {
expect(find.byType(DocumentBanner), findsNothing);
}
Future<void> tapRestoreButton() async {
final restoreButton = find.textContaining(
LocaleKeys.deletePagePrompt_restore.tr(),
);
await tapButton(restoreButton);
}
Future<void> tapDeletePermanentlyButton() async {
final restoreButton = find.textContaining(
LocaleKeys.deletePagePrompt_deletePermanent.tr(),
);
await tapButton(restoreButton);
}
Future<void> tapShareButton() async {
final shareButton = find.byWidgetPredicate(
(widget) => widget is DocumentShareButton,
);
await tapButton(shareButton);
}
Future<void> tapMarkdownButton() async {
final markdownButton = find.textContaining(
LocaleKeys.shareAction_markdown.tr(),
);
await tapButton(markdownButton);
}
void expectToExportSuccess() {
final exportSuccess = find.byWidgetPredicate(
(widget) =>
widget is FlowyText &&
widget.title == LocaleKeys.settings_files_exportFileSuccess.tr(),
);
expect(exportSuccess, findsOneWidget);
}
Future<void> hoverOnCoverPluginAddButton() async {
final editor = find.byWidgetPredicate(
(widget) => widget is AppFlowyEditor,
);
final gesture = await createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer(location: Offset.zero);
addTearDown(gesture.removePointer);
await pump();
final topLeft = getTopLeft(editor).translate(20, 20);
await gesture.moveTo(topLeft);
await pumpAndSettle();
}
Future<void> expectToSeePluginAddCoverAndIconButton() async {
final addCover = find.textContaining(
LocaleKeys.document_plugins_cover_addCover.tr(),
);
final addIcon = find.textContaining(
LocaleKeys.document_plugins_cover_addIcon.tr(),
);
expect(addCover, findsOneWidget);
expect(addIcon, findsOneWidget);
}
void expectToSeeUserName(String name) {
final userName = find.byWidgetPredicate(
(widget) => widget is FlowyText && widget.title == name,
);
expect(userName, findsOneWidget);
}
}
@@ -1,6 +1,6 @@
import 'dart:io';
import 'package:appflowy/core/config/kv_keys.dart';
import 'package:appflowy/workspace/application/settings/settings_location_cubit.dart';
import 'package:archive/archive_io.dart';
import 'package:flutter/services.dart';
import 'package:path/path.dart' as p;
@@ -49,11 +49,10 @@ class TestWorkspaceService {
/// Instructs the application to read workspace data from the workspace found under this [TestWorkspace]'s path.
Future<void> setUpAll() async {
final root = await workspace.root;
final path = root.path;
SharedPreferences.setMockInitialValues(
{
KVKeys.pathLocation: path,
kSettingsLocationDefaultLocation:
await workspace.root.then((value) => value.path),
},
);
}
@@ -0,0 +1,23 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/presentation/home/home_stack.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_test/flutter_test.dart';
import 'base.dart';
extension AppFlowyLaunch on WidgetTester {
Future<void> tapGoButton() async {
await tapButtonWithName(LocaleKeys.letsGoButtonText.tr());
return;
}
Future<void> tapCreateButton() async {
await tapButtonWithName(LocaleKeys.settings_files_create.tr());
return;
}
Future<void> expectToSeeWelcomePage() async {
expect(find.byType(HomeStack), findsOneWidget);
expect(find.textContaining('Read me'), findsNWidgets(2));
}
}
@@ -1,10 +1,10 @@
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/util/file_picker/file_picker_impl.dart';
import 'package:appflowy/util/file_picker/file_picker_service.dart';
import 'package:file_picker/src/file_picker.dart' as fp;
import 'package:path/path.dart' as p;
import '../util.dart';
class MockFilePicker implements FilePickerService {
class MockFilePicker extends FilePicker {
MockFilePicker({
required this.mockPath,
});
@@ -15,34 +15,6 @@ class MockFilePicker implements FilePickerService {
Future<String?> getDirectoryPath({String? title}) {
return Future.value(mockPath);
}
@override
Future<String?> saveFile({
String? dialogTitle,
String? fileName,
String? initialDirectory,
fp.FileType type = fp.FileType.any,
List<String>? allowedExtensions,
bool lockParentWindow = false,
}) {
return Future.value(mockPath);
}
@override
Future<FilePickerResult?> pickFiles({
String? dialogTitle,
String? initialDirectory,
fp.FileType type = fp.FileType.any,
List<String>? allowedExtensions,
Function(fp.FilePickerStatus p1)? onFileLoading,
bool allowCompression = true,
bool allowMultiple = false,
bool withData = false,
bool withReadStream = false,
bool lockParentWindow = false,
}) {
throw UnimplementedError();
}
}
Future<void> mockGetDirectoryPath(String? name) async {
@@ -55,15 +27,3 @@ Future<void> mockGetDirectoryPath(String? name) async {
);
return;
}
Future<String> mockSaveFilePath(String? name, String fileName) async {
final dir = await TestFolder.testLocation(name);
final path = p.join(dir.path, fileName);
getIt.unregister<FilePickerService>();
getIt.registerFactory<FilePickerService>(
() => MockFilePicker(
mockPath: path,
),
);
return path;
}
@@ -1,8 +1,8 @@
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/openai_client.dart';
import 'package:appflowy/plugins/document/presentation/plugins/openai/service/openai_client.dart';
import 'package:mocktail/mocktail.dart';
import 'dart:convert';
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/text_completion.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/error.dart';
import 'package:appflowy/plugins/document/presentation/plugins/openai/service/text_completion.dart';
import 'package:appflowy/plugins/document/presentation/plugins/openai/service/error.dart';
import 'package:http/http.dart' as http;
import 'dart:async';
@@ -1,13 +1,33 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/workspace/presentation/settings/settings_dialog.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu_element.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_test/flutter_test.dart';
import 'base.dart';
enum SettingsPage {
appearance,
language,
files,
user,
}
extension on SettingsPage {
String get name {
switch (this) {
case SettingsPage.appearance:
return LocaleKeys.settings_menu_appearance.tr();
case SettingsPage.language:
return LocaleKeys.settings_menu_language.tr();
case SettingsPage.files:
return LocaleKeys.settings_menu_files.tr();
case SettingsPage.user:
return LocaleKeys.settings_menu_user.tr();
}
}
}
extension AppFlowySettings on WidgetTester {
/// Open settings page
Future<void> openSettings() async {
@@ -21,9 +41,7 @@ extension AppFlowySettings on WidgetTester {
/// Open the page that insides the settings page
Future<void> openSettingsPage(SettingsPage page) async {
final button = find.byWidgetPredicate(
(widget) => widget is SettingsMenuElement && widget.page == page,
);
final button = find.text(page.name, findRichText: true);
expect(button, findsOneWidget);
await tapButton(button);
return;
@@ -32,7 +50,7 @@ extension AppFlowySettings on WidgetTester {
/// Restore the AppFlowy data storage location
Future<void> restoreLocation() async {
final button =
find.byTooltip(LocaleKeys.settings_files_recoverLocationTooltips.tr());
find.byTooltip(LocaleKeys.settings_files_restoreLocation.tr());
expect(button, findsOneWidget);
await tapButton(button);
return;
@@ -46,9 +64,8 @@ extension AppFlowySettings on WidgetTester {
}
Future<void> tapCustomLocationButton() async {
final button = find.byTooltip(
LocaleKeys.settings_files_changeLocationTooltips.tr(),
);
final button =
find.byTooltip(LocaleKeys.settings_files_customizeLocation.tr());
expect(button, findsOneWidget);
await tapButton(button);
return;
@@ -1,4 +1,4 @@
export 'base.dart';
export 'common_operations.dart';
export 'launch.dart';
export 'settings.dart';
export 'data.dart';
@@ -1,38 +0,0 @@
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/protobuf/flowy-config/entities.pb.dart';
class Config {
static Future<void> setSupabaseConfig({
required String url,
required String anonKey,
required String key,
required String secret,
}) async {
await ConfigEventSetSupabaseConfig(
SupabaseConfigPB.create()
..supabaseUrl = url
..key = key
..anonKey = anonKey
..jwtSecret = secret,
).send();
}
static Future<void> setSupabaseCollabPluginConfig({
required String url,
required String key,
required String jwtSecret,
required String collabTable,
}) async {
final payload = CollabPluginConfigPB.create();
final collabTableConfig = CollabTableConfigPB.create()
..tableName = collabTable;
payload.supabaseConfig = SupabaseDBConfigPB.create()
..supabaseUrl = url
..key = key
..jwtSecret = jwtSecret
..collabTableConfig = collabTableConfig;
await ConfigEventSetCollabPluginConfig(payload).send();
}
}
@@ -1,85 +0,0 @@
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/protobuf/flowy-config/entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:shared_preferences/shared_preferences.dart';
abstract class KeyValueStorage {
Future<void> set(String key, String value);
Future<Either<FlowyError, String>> get(String key);
Future<void> remove(String key);
Future<void> clear();
}
class DartKeyValue implements KeyValueStorage {
SharedPreferences? _sharedPreferences;
SharedPreferences get sharedPreferences => _sharedPreferences!;
@override
Future<Either<FlowyError, String>> get(String key) async {
await _initSharedPreferencesIfNeeded();
final value = sharedPreferences.getString(key);
if (value != null) {
return Right(value);
}
return Left(FlowyError());
}
@override
Future<void> remove(String key) async {
await _initSharedPreferencesIfNeeded();
await sharedPreferences.remove(key);
}
@override
Future<void> set(String key, String value) async {
await _initSharedPreferencesIfNeeded();
await sharedPreferences.setString(key, value);
}
@override
Future<void> clear() async {
await _initSharedPreferencesIfNeeded();
await sharedPreferences.clear();
}
Future<void> _initSharedPreferencesIfNeeded() async {
_sharedPreferences ??= await SharedPreferences.getInstance();
}
}
/// Key-value store
/// The data is stored in the local storage of the device.
class RustKeyValue implements KeyValueStorage {
@override
Future<void> set(String key, String value) async {
await ConfigEventSetKeyValue(
KeyValuePB.create()
..key = key
..value = value,
).send();
}
@override
Future<Either<FlowyError, String>> get(String key) async {
final payload = KeyPB.create()..key = key;
final response = await ConfigEventGetKeyValue(payload).send();
return response.swap().map((r) => r.value);
}
@override
Future<void> remove(String key) async {
await ConfigEventRemoveKeyValue(
KeyPB.create()..key = key,
).send();
}
@override
Future<void> clear() async {
// TODO(Lucas): implement clear
}
}
@@ -1,21 +0,0 @@
class KVKeys {
const KVKeys._();
static const String prefix = 'io.appflowy.appflowy_flutter';
/// The key for the path location of the local data for the whole app.
static const String pathLocation = '$prefix.path_location';
/// The key for the last time login type.
///
/// The value is one of the following:
/// - local
/// - supabase
static const String loginType = '$prefix.login_type';
/// The key for saving the window size
///
/// The value is a json string with the following format:
/// {'height': 600.0, 'width': 800.0}
static const String windowSize = 'windowSize';
}
@@ -3,7 +3,7 @@ import 'dart:typed_data';
import 'package:appflowy_backend/protobuf/flowy-notification/protobuf.dart';
import 'package:dartz/dartz.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/notification.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/notification.pb.dart';
import 'package:appflowy_backend/rust_stream.dart';
import 'notification_helper.dart';
@@ -3,7 +3,7 @@ import 'dart:typed_data';
import 'package:appflowy_backend/protobuf/flowy-notification/protobuf.dart';
import 'package:dartz/dartz.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/notification.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/notification.pb.dart';
import 'package:appflowy_backend/rust_stream.dart';
import 'notification_helper.dart';
@@ -1,24 +0,0 @@
import 'dart:typed_data';
import 'package:appflowy/core/notification/notification_helper.dart';
import 'package:appflowy_backend/protobuf/flowy-document2/notification.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:dartz/dartz.dart';
typedef DocumentNotificationCallback = void Function(
DocumentNotification,
Either<Uint8List, FlowyError>,
);
class DocumentNotificationParser
extends NotificationParser<DocumentNotification, FlowyError> {
DocumentNotificationParser({
String? id,
required DocumentNotificationCallback callback,
}) : super(
id: id,
callback: callback,
tyParser: (ty) => DocumentNotification.valueOf(ty),
errorParser: (bytes) => FlowyError.fromBuffer(bytes),
);
}
-45
View File
@@ -1,45 +0,0 @@
// lib/env/env.dart
import 'package:envied/envied.dart';
part 'env.g.dart';
@Envied(path: '.env')
abstract class Env {
@EnviedField(
obfuscate: true,
varName: 'SUPABASE_URL',
defaultValue: '',
)
static final String supabaseUrl = _Env.supabaseUrl;
@EnviedField(
obfuscate: true,
varName: 'SUPABASE_ANON_KEY',
defaultValue: '',
)
static final String supabaseAnonKey = _Env.supabaseAnonKey;
@EnviedField(
obfuscate: true,
varName: 'SUPABASE_KEY',
defaultValue: '',
)
static final String supabaseKey = _Env.supabaseKey;
@EnviedField(
obfuscate: true,
varName: 'SUPABASE_JWT_SECRET',
defaultValue: '',
)
static final String supabaseJwtSecret = _Env.supabaseJwtSecret;
@EnviedField(
obfuscate: true,
varName: 'SUPABASE_COLLAB_TABLE',
defaultValue: '',
)
static final String supabaseCollabTable = _Env.supabaseCollabTable;
}
bool get isSupabaseEnable =>
Env.supabaseUrl.isNotEmpty &&
Env.supabaseAnonKey.isNotEmpty &&
Env.supabaseKey.isNotEmpty &&
Env.supabaseJwtSecret.isNotEmpty;
+28 -1
View File
@@ -1,10 +1,37 @@
import 'package:appflowy/startup/entry_point.dart';
import 'package:appflowy_backend/log.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:hotkey_manager/hotkey_manager.dart';
import 'startup/launch_configuration.dart';
import 'startup/startup.dart';
import 'user/presentation/splash_screen.dart';
import 'window/window.dart';
class FlowyApp implements EntryPoint {
@override
Widget create(LaunchConfiguration config) {
return SplashScreen(
autoRegister: config.autoRegistrationSupported,
);
}
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// Handle platform errors not caught by Flutter.
// Reduces the likelihood of the app crashing, and logs the error.
PlatformDispatcher.instance.onError = (error, stack) {
Log.error('Uncaught platform error', error, stack);
return true;
};
await EasyLocalization.ensureInitialized();
await hotKeyManager.unregisterAll();
await AppWindow.initialize();
await FlowyRunner.run(FlowyApp());
}
@@ -28,22 +28,21 @@ class BlankPluginConfig implements PluginConfig {
class BlankPagePlugin extends Plugin {
@override
PluginWidgetBuilder get widgetBuilder => BlankPagePluginWidgetBuilder();
PluginDisplay get display => BlankPagePluginDisplay();
@override
PluginId get id => "BlankStack";
@override
PluginType get pluginType => PluginType.blank;
PluginType get ty => PluginType.blank;
}
class BlankPagePluginWidgetBuilder extends PluginWidgetBuilder
with NavigationItem {
class BlankPagePluginDisplay extends PluginDisplay with NavigationItem {
@override
Widget get leftBarItem => FlowyText.medium(LocaleKeys.blankPageTitle.tr());
@override
Widget buildWidget({PluginContext? context}) => const BlankPage();
Widget buildWidget(PluginContext context) => const BlankPage();
@override
List<NavigationItem> get navigationItems => [this];
@@ -1,6 +1,6 @@
part of 'cell_service.dart';
typedef CellContextByFieldId = LinkedHashMap<String, DatabaseCellContext>;
typedef CellByFieldId = LinkedHashMap<String, CellIdentifier>;
class DatabaseCell {
dynamic object;
@@ -13,7 +13,7 @@ class DatabaseCell {
/// We use [fieldId + rowId] to identify the cell.
class CellCacheKey {
final String fieldId;
final RowId rowId;
final String rowId;
CellCacheKey({
required this.fieldId,
required this.rowId,
@@ -28,7 +28,7 @@ class CellCache {
final String viewId;
/// fieldId: {cacheKey: GridCell}
final Map<String, Map<RowId, dynamic>> _cellDataByFieldId = {};
final Map<String, Map<String, dynamic>> _cellDataByFieldId = {};
CellCache({
required this.viewId,
});
@@ -38,7 +38,7 @@ class CellCache {
}
void remove(CellCacheKey key) {
final map = _cellDataByFieldId[key.fieldId];
var map = _cellDataByFieldId[key.fieldId];
if (map != null) {
map.remove(key.rowId);
}
@@ -1,8 +1,7 @@
import 'dart:async';
import 'package:appflowy/plugins/database_view/application/field/field_listener.dart';
import 'package:appflowy/plugins/database_view/application/row/row_service.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';
@@ -22,7 +21,7 @@ import 'cell_service.dart';
///
// ignore: must_be_immutable
class CellController<T, D> extends Equatable {
final DatabaseCellContext cellContext;
final CellIdentifier cellId;
final CellCache _cellCache;
final CellCacheKey _cacheKey;
final FieldBackendService _fieldBackendSvc;
@@ -37,37 +36,37 @@ class CellController<T, D> extends Equatable {
Timer? _loadDataOperation;
Timer? _saveDataOperation;
String get viewId => cellContext.viewId;
String get viewId => cellId.viewId;
RowId get rowId => cellContext.rowId;
String get rowId => cellId.rowId;
String get fieldId => cellContext.fieldInfo.id;
String get fieldId => cellId.fieldInfo.id;
FieldInfo get fieldInfo => cellContext.fieldInfo;
FieldInfo get fieldInfo => cellId.fieldInfo;
FieldType get fieldType => cellContext.fieldInfo.fieldType;
FieldType get fieldType => cellId.fieldInfo.fieldType;
CellController({
required this.cellContext,
required this.cellId,
required CellCache cellCache,
required CellDataLoader<T> cellDataLoader,
required CellDataPersistence<D> cellDataPersistence,
}) : _cellCache = cellCache,
_cellDataLoader = cellDataLoader,
_cellDataPersistence = cellDataPersistence,
_fieldListener = SingleFieldListener(fieldId: cellContext.fieldId),
_fieldListener = SingleFieldListener(fieldId: cellId.fieldId),
_fieldBackendSvc = FieldBackendService(
viewId: cellContext.viewId,
fieldId: cellContext.fieldInfo.id,
viewId: cellId.viewId,
fieldId: cellId.fieldInfo.id,
),
_cacheKey = CellCacheKey(
rowId: cellContext.rowId,
fieldId: cellContext.fieldInfo.id,
rowId: cellId.rowId,
fieldId: cellId.fieldInfo.id,
) {
_cellDataNotifier = CellDataNotifier(value: _cellCache.get(_cacheKey));
_cellListener = CellListener(
rowId: cellContext.rowId,
fieldId: cellContext.fieldInfo.id,
rowId: cellId.rowId,
fieldId: cellId.fieldInfo.id,
);
/// 1.Listen on user edit event and load the new cell data if needed.
@@ -195,10 +194,8 @@ class CellController<T, D> extends Equatable {
}
@override
List<Object> get props => [
_cellCache.get(_cacheKey) ?? "",
cellContext.rowId + cellContext.fieldInfo.id
];
List<Object> get props =>
[_cellCache.get(_cacheKey) ?? "", cellId.rowId + cellId.fieldInfo.id];
}
class CellDataNotifier<T> extends ChangeNotifier {
@@ -1,8 +1,7 @@
import 'package:appflowy_backend/protobuf/flowy-database2/checklist_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/date_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/select_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/url_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/date_type_option_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/url_type_option_entities.pb.dart';
import 'cell_controller.dart';
import 'cell_service.dart';
@@ -12,116 +11,96 @@ typedef CheckboxCellController = CellController<String, String>;
typedef NumberCellController = CellController<String, String>;
typedef SelectOptionCellController
= CellController<SelectOptionCellDataPB, String>;
typedef ChecklistCellController = CellController<ChecklistCellDataPB, String>;
typedef ChecklistCellController
= CellController<SelectOptionCellDataPB, String>;
typedef DateCellController = CellController<DateCellDataPB, DateCellData>;
typedef URLCellController = CellController<URLCellDataPB, String>;
class CellControllerBuilder {
final DatabaseCellContext _cellContext;
final CellIdentifier _cellId;
final CellCache _cellCache;
CellControllerBuilder({
required DatabaseCellContext cellContext,
required CellIdentifier cellId,
required CellCache cellCache,
}) : _cellCache = cellCache,
_cellContext = cellContext;
_cellId = cellId;
CellController build() {
switch (_cellContext.fieldType) {
switch (_cellId.fieldType) {
case FieldType.Checkbox:
final cellDataLoader = CellDataLoader(
cellContext: _cellContext,
cellId: _cellId,
parser: StringCellDataParser(),
);
return TextCellController(
cellContext: _cellContext,
cellId: _cellId,
cellCache: _cellCache,
cellDataLoader: cellDataLoader,
cellDataPersistence:
TextCellDataPersistence(cellContext: _cellContext),
cellDataPersistence: TextCellDataPersistence(cellId: _cellId),
);
case FieldType.DateTime:
case FieldType.LastEditedTime:
case FieldType.CreatedTime:
final cellDataLoader = CellDataLoader(
cellContext: _cellContext,
cellId: _cellId,
parser: DateCellDataParser(),
reloadOnFieldChanged: true,
);
return DateCellController(
cellContext: _cellContext,
cellId: _cellId,
cellCache: _cellCache,
cellDataLoader: cellDataLoader,
cellDataPersistence:
DateCellDataPersistence(cellContext: _cellContext),
cellDataPersistence: DateCellDataPersistence(cellId: _cellId),
);
case FieldType.Number:
final cellDataLoader = CellDataLoader(
cellContext: _cellContext,
cellId: _cellId,
parser: NumberCellDataParser(),
reloadOnFieldChanged: true,
);
return NumberCellController(
cellContext: _cellContext,
cellId: _cellId,
cellCache: _cellCache,
cellDataLoader: cellDataLoader,
cellDataPersistence:
TextCellDataPersistence(cellContext: _cellContext),
cellDataPersistence: TextCellDataPersistence(cellId: _cellId),
);
case FieldType.RichText:
final cellDataLoader = CellDataLoader(
cellContext: _cellContext,
cellId: _cellId,
parser: StringCellDataParser(),
);
return TextCellController(
cellContext: _cellContext,
cellId: _cellId,
cellCache: _cellCache,
cellDataLoader: cellDataLoader,
cellDataPersistence:
TextCellDataPersistence(cellContext: _cellContext),
cellDataPersistence: TextCellDataPersistence(cellId: _cellId),
);
case FieldType.MultiSelect:
case FieldType.SingleSelect:
case FieldType.Checklist:
final cellDataLoader = CellDataLoader(
cellContext: _cellContext,
cellId: _cellId,
parser: SelectOptionCellDataParser(),
reloadOnFieldChanged: true,
);
return SelectOptionCellController(
cellContext: _cellContext,
cellId: _cellId,
cellCache: _cellCache,
cellDataLoader: cellDataLoader,
cellDataPersistence:
TextCellDataPersistence(cellContext: _cellContext),
cellDataPersistence: TextCellDataPersistence(cellId: _cellId),
);
case FieldType.Checklist:
final cellDataLoader = CellDataLoader(
cellContext: _cellContext,
parser: ChecklistCellDataParser(),
reloadOnFieldChanged: true,
);
return ChecklistCellController(
cellContext: _cellContext,
cellCache: _cellCache,
cellDataLoader: cellDataLoader,
cellDataPersistence:
TextCellDataPersistence(cellContext: _cellContext),
);
case FieldType.URL:
final cellDataLoader = CellDataLoader(
cellContext: _cellContext,
cellId: _cellId,
parser: URLCellDataParser(),
);
return URLCellController(
cellContext: _cellContext,
cellId: _cellId,
cellCache: _cellCache,
cellDataLoader: cellDataLoader,
cellDataPersistence:
TextCellDataPersistence(cellContext: _cellContext),
cellDataPersistence: TextCellDataPersistence(cellId: _cellId),
);
}
throw UnimplementedError;
@@ -11,18 +11,18 @@ abstract class CellDataParser<T> {
class CellDataLoader<T> {
final CellBackendService service = CellBackendService();
final DatabaseCellContext cellContext;
final CellIdentifier cellId;
final CellDataParser<T> parser;
final bool reloadOnFieldChanged;
CellDataLoader({
required this.cellContext,
required this.cellId,
required this.parser,
this.reloadOnFieldChanged = false,
});
Future<T?> loadData() {
final fut = service.getCell(cellContext: cellContext);
final fut = service.getCell(cellId: cellId);
return fut.then(
(result) => result.fold(
(CellPB cell) {
@@ -84,16 +84,6 @@ class SelectOptionCellDataParser
}
}
class ChecklistCellDataParser implements CellDataParser<ChecklistCellDataPB> {
@override
ChecklistCellDataPB? parserData(List<int> data) {
if (data.isEmpty) {
return null;
}
return ChecklistCellDataPB.fromBuffer(data);
}
}
class URLCellDataParser implements CellDataParser<URLCellDataPB> {
@override
URLCellDataPB? parserData(List<int> data) {
@@ -7,19 +7,16 @@ abstract class CellDataPersistence<D> {
}
class TextCellDataPersistence implements CellDataPersistence<String> {
final DatabaseCellContext cellContext;
final CellIdentifier cellId;
final _cellBackendSvc = CellBackendService();
TextCellDataPersistence({
required this.cellContext,
required this.cellId,
});
@override
Future<Option<FlowyError>> save(String data) async {
final fut = _cellBackendSvc.updateCell(
cellContext: cellContext,
data: data,
);
final fut = _cellBackendSvc.updateCell(cellId: cellId, data: data);
return fut.then((result) {
return result.fold(
(l) => none(),
@@ -32,30 +29,30 @@ class TextCellDataPersistence implements CellDataPersistence<String> {
@freezed
class DateCellData with _$DateCellData {
const factory DateCellData({
DateTime? dateTime,
required DateTime date,
String? time,
required bool includeTime,
}) = _DateCellData;
}
class DateCellDataPersistence implements CellDataPersistence<DateCellData> {
final DatabaseCellContext cellContext;
final CellIdentifier cellId;
DateCellDataPersistence({
required this.cellContext,
required this.cellId,
});
@override
Future<Option<FlowyError>> save(DateCellData data) {
final payload = DateChangesetPB.create()
..cellId = _makeCellPath(cellContext);
if (data.dateTime != null) {
final date = (data.dateTime!.millisecondsSinceEpoch ~/ 1000).toString();
payload.date = date;
}
var payload = DateChangesetPB.create()..cellPath = _makeCellPath(cellId);
final date = (data.date.millisecondsSinceEpoch ~/ 1000).toString();
payload.date = date;
payload.isUtc = data.date.isUtc;
payload.includeTime = data.includeTime;
if (data.time != null) {
payload.time = data.time!;
}
payload.includeTime = data.includeTime;
return DatabaseEventUpdateDateCell(payload).send().then((result) {
return result.fold(
@@ -66,7 +63,7 @@ class DateCellDataPersistence implements CellDataPersistence<DateCellData> {
}
}
CellIdPB _makeCellPath(DatabaseCellContext cellId) {
CellIdPB _makeCellPath(CellIdentifier cellId) {
return CellIdPB.create()
..viewId = cellId.viewId
..fieldId = cellId.fieldId
@@ -1,17 +1,15 @@
import 'package:appflowy/core/notification/grid_notification.dart';
import 'package:appflowy/core/grid_notification.dart';
import 'package:dartz/dartz.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/notification.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/notification.pb.dart';
import 'package:flowy_infra/notifier.dart';
import 'dart:async';
import 'dart:typed_data';
import '../row/row_service.dart';
typedef UpdateFieldNotifiedValue = Either<Unit, FlowyError>;
class CellListener {
final RowId rowId;
final String rowId;
final String fieldId;
PublishNotifier<UpdateFieldNotifiedValue>? _updateCellNotifier =
PublishNotifier();
@@ -1,21 +1,19 @@
import 'dart:async';
import 'dart:collection';
import 'package:appflowy_backend/protobuf/flowy-database2/checklist_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/date_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/select_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/url_entities.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/cell_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/cell_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/date_type_option_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/url_type_option_entities.pb.dart';
import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:convert' show utf8;
import '../field/field_controller.dart';
import '../row/row_service.dart';
part 'cell_service.freezed.dart';
part 'cell_data_loader.dart';
part 'cell_cache.dart';
@@ -25,39 +23,40 @@ class CellBackendService {
CellBackendService();
Future<Either<void, FlowyError>> updateCell({
required DatabaseCellContext cellContext,
required CellIdentifier cellId,
required String data,
}) {
final payload = CellChangesetPB.create()
..viewId = cellContext.viewId
..fieldId = cellContext.fieldId
..rowId = cellContext.rowId
..cellChangeset = data;
..viewId = cellId.viewId
..fieldId = cellId.fieldId
..rowId = cellId.rowId
..typeCellData = data;
return DatabaseEventUpdateCell(payload).send();
}
Future<Either<CellPB, FlowyError>> getCell({
required DatabaseCellContext cellContext,
required CellIdentifier cellId,
}) {
final payload = CellIdPB.create()
..viewId = cellContext.viewId
..fieldId = cellContext.fieldId
..rowId = cellContext.rowId;
..viewId = cellId.viewId
..fieldId = cellId.fieldId
..rowId = cellId.rowId;
return DatabaseEventGetCell(payload).send();
}
}
/// Id of the cell
/// We can locate the cell by using database + rowId + field.id.
@freezed
class DatabaseCellContext with _$DatabaseCellContext {
const factory DatabaseCellContext({
class CellIdentifier with _$CellIdentifier {
const factory CellIdentifier({
required String viewId,
required RowId rowId,
required String rowId,
required FieldInfo fieldInfo,
}) = _DatabaseCellContext;
}) = _CellIdentifier;
// ignore: unused_element
const DatabaseCellContext._();
const CellIdentifier._();
String get fieldId => fieldInfo.id;
@@ -1,71 +0,0 @@
import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/cell_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/checklist_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/select_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:dartz/dartz.dart';
class ChecklistCellBackendService {
final DatabaseCellContext cellContext;
ChecklistCellBackendService({required this.cellContext});
Future<Either<Unit, FlowyError>> create({
required String name,
}) {
final payload = ChecklistCellDataChangesetPB.create()
..viewId = cellContext.viewId
..fieldId = cellContext.fieldInfo.id
..rowId = cellContext.rowId
..insertOptions.add(name);
return DatabaseEventUpdateChecklistCell(payload).send();
}
Future<Either<Unit, FlowyError>> delete({
required List<String> optionIds,
}) {
final payload = ChecklistCellDataChangesetPB.create()
..viewId = cellContext.viewId
..fieldId = cellContext.fieldInfo.id
..rowId = cellContext.rowId
..deleteOptionIds.addAll(optionIds);
return DatabaseEventUpdateChecklistCell(payload).send();
}
Future<Either<Unit, FlowyError>> select({
required String optionId,
}) {
final payload = ChecklistCellDataChangesetPB.create()
..viewId = cellContext.viewId
..fieldId = cellContext.fieldInfo.id
..rowId = cellContext.rowId
..selectedOptionIds.add(optionId);
return DatabaseEventUpdateChecklistCell(payload).send();
}
Future<Either<Unit, FlowyError>> update({
required SelectOptionPB option,
}) {
final payload = ChecklistCellDataChangesetPB.create()
..viewId = cellContext.viewId
..fieldId = cellContext.fieldInfo.id
..rowId = cellContext.rowId
..updateOptions.add(option);
return DatabaseEventUpdateChecklistCell(payload).send();
}
Future<Either<ChecklistCellDataPB, FlowyError>> getCellData() {
final payload = CellIdPB.create()
..fieldId = cellContext.fieldInfo.id
..viewId = cellContext.viewId
..rowId = cellContext.rowId
..rowId = cellContext.rowId;
return DatabaseEventGetChecklistCellData(payload).send();
}
}
@@ -2,14 +2,14 @@ import 'package:appflowy/plugins/database_view/application/field/field_controlle
import 'package:appflowy/plugins/database_view/application/layout/calendar_setting_listener.dart';
import 'package:appflowy/plugins/database_view/application/view/view_cache.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/calendar_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/group.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/group_changeset.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/row_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/calendar_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-database/group.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/group_changeset.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/setting_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:collection/collection.dart';
import 'dart:async';
import 'package:dartz/dartz.dart';
@@ -18,7 +18,6 @@ import 'defines.dart';
import 'layout/layout_setting_listener.dart';
import 'row/row_cache.dart';
import 'group/group_listener.dart';
import 'row/row_service.dart';
typedef OnGroupByField = void Function(List<GroupPB>);
typedef OnUpdateGroup = void Function(List<GroupPB>);
@@ -39,18 +38,18 @@ class GroupCallbacks {
});
}
class DatabaseLayoutSettingCallbacks {
final void Function(DatabaseLayoutSettingPB) onLayoutChanged;
final void Function(DatabaseLayoutSettingPB) onLoadLayout;
class LayoutCallbacks {
final void Function(LayoutSettingPB) onLayoutChanged;
final void Function(LayoutSettingPB) onLoadLayout;
DatabaseLayoutSettingCallbacks({
LayoutCallbacks({
required this.onLayoutChanged,
required this.onLoadLayout,
});
}
class CalendarLayoutCallbacks {
final void Function(DatabaseLayoutSettingPB) onCalendarLayoutChanged;
final void Function(LayoutSettingPB) onCalendarLayoutChanged;
CalendarLayoutCallbacks({required this.onCalendarLayoutChanged});
}
@@ -59,14 +58,14 @@ class DatabaseCallbacks {
OnDatabaseChanged? onDatabaseChanged;
OnFieldsChanged? onFieldsChanged;
OnFiltersChanged? onFiltersChanged;
OnNumOfRowsChanged? onNumOfRowsChanged;
OnRowsChanged? onRowsChanged;
OnRowsDeleted? onRowsDeleted;
OnRowsUpdated? onRowsUpdated;
OnRowsCreated? onRowsCreated;
DatabaseCallbacks({
this.onDatabaseChanged,
this.onNumOfRowsChanged,
this.onRowsChanged,
this.onFieldsChanged,
this.onFiltersChanged,
this.onRowsUpdated,
@@ -79,31 +78,30 @@ class DatabaseController {
final String viewId;
final DatabaseViewBackendService _databaseViewBackendSvc;
final FieldController fieldController;
DatabaseLayoutPB? databaseLayout;
DatabaseLayoutSettingPB? databaseLayoutSetting;
late DatabaseViewCache _viewCache;
final LayoutTypePB layoutType;
// Callbacks
DatabaseCallbacks? _databaseCallbacks;
GroupCallbacks? _groupCallbacks;
DatabaseLayoutSettingCallbacks? _layoutCallbacks;
LayoutCallbacks? _layoutCallbacks;
CalendarLayoutCallbacks? _calendarLayoutCallbacks;
// Getters
RowCache get rowCache => _viewCache.rowCache;
// Listener
final DatabaseGroupListener _groupListener;
final DatabaseLayoutSettingListener _layoutListener;
final DatabaseCalendarLayoutListener _calendarLayoutListener;
final DatabaseGroupListener groupListener;
final DatabaseLayoutListener layoutListener;
final DatabaseCalendarLayoutListener calendarLayoutListener;
DatabaseController({required ViewPB view})
DatabaseController({required ViewPB view, required this.layoutType})
: viewId = view.id,
_databaseViewBackendSvc = DatabaseViewBackendService(viewId: view.id),
fieldController = FieldController(viewId: view.id),
_groupListener = DatabaseGroupListener(view.id),
_layoutListener = DatabaseLayoutSettingListener(view.id),
_calendarLayoutListener = DatabaseCalendarLayoutListener(view.id) {
groupListener = DatabaseGroupListener(view.id),
layoutListener = DatabaseLayoutListener(view.id),
calendarLayoutListener = DatabaseCalendarLayoutListener(view.id) {
_viewCache = DatabaseViewCache(
viewId: viewId,
fieldController: fieldController,
@@ -112,11 +110,14 @@ class DatabaseController {
_listenOnFieldsChanged();
_listenOnGroupChanged();
_listenOnLayoutChanged();
if (layoutType == LayoutTypePB.Calendar) {
_listenOnCalendarLayoutChanged();
}
}
void setListener({
DatabaseCallbacks? onDatabaseChanged,
DatabaseLayoutSettingCallbacks? onLayoutChanged,
LayoutCallbacks? onLayoutChanged,
GroupCallbacks? onGroupChanged,
CalendarLayoutCallbacks? onCalendarLayoutChanged,
}) {
@@ -130,12 +131,6 @@ class DatabaseController {
return _databaseViewBackendSvc.openGrid().then((result) {
return result.fold(
(database) async {
databaseLayout = database.layoutType;
if (databaseLayout == DatabaseLayoutPB.Calendar) {
_listenOnCalendarLayoutChanged();
}
_databaseCallbacks?.onDatabaseChanged?.call(database);
_viewCache.rowCache.setInitialRows(database.rows);
return await fieldController
@@ -161,7 +156,7 @@ class DatabaseController {
}
Future<Either<RowPB, FlowyError>> createRow({
RowId? startRowId,
String? startRowId,
String? groupId,
void Function(RowDataBuilder builder)? withCells,
}) {
@@ -180,28 +175,18 @@ class DatabaseController {
);
}
Future<Either<Unit, FlowyError>> moveGroupRow({
Future<Either<Unit, FlowyError>> moveRow({
required RowPB fromRow,
required String groupId,
RowPB? toRow,
}) {
return _databaseViewBackendSvc.moveGroupRow(
return _databaseViewBackendSvc.moveRow(
fromRowId: fromRow.id,
toGroupId: groupId,
toRowId: toRow?.id,
);
}
Future<Either<Unit, FlowyError>> moveRow({
required RowPB fromRow,
required RowPB toRow,
}) {
return _databaseViewBackendSvc.moveRow(
fromRowId: fromRow.id,
toRowId: toRow.id,
);
}
Future<Either<Unit, FlowyError>> moveGroup({
required String fromGroupId,
required String toGroupId,
@@ -213,7 +198,7 @@ class DatabaseController {
}
Future<void> updateCalenderLayoutSetting(
CalendarLayoutSettingPB layoutSetting,
CalendarLayoutSettingsPB layoutSetting,
) async {
await _databaseViewBackendSvc
.updateLayoutSetting(calendarLayoutSetting: layoutSetting)
@@ -225,7 +210,7 @@ class DatabaseController {
Future<void> dispose() async {
await _databaseViewBackendSvc.closeView();
await fieldController.dispose();
await _groupListener.stop();
await groupListener.stop();
await _viewCache.dispose();
_databaseCallbacks = null;
_groupCallbacks = null;
@@ -246,25 +231,20 @@ class DatabaseController {
}
Future<void> _loadLayoutSetting() async {
if (databaseLayout != null) {
_databaseViewBackendSvc.getLayoutSetting(databaseLayout!).then((result) {
result.fold(
(newDatabaseLayoutSetting) {
databaseLayoutSetting = newDatabaseLayoutSetting;
databaseLayoutSetting?.freeze();
_layoutCallbacks?.onLoadLayout(newDatabaseLayoutSetting);
},
(r) => Log.error(r),
);
});
}
_databaseViewBackendSvc.getLayoutSetting(layoutType).then((result) {
result.fold(
(l) {
_layoutCallbacks?.onLoadLayout(l);
},
(r) => Log.error(r),
);
});
}
void _listenOnRowsChanged() {
final callbacks = DatabaseViewCallbacks(
onNumOfRowsChanged: (rows, rowByRowId, reason) {
_databaseCallbacks?.onNumOfRowsChanged?.call(rows, rowByRowId, reason);
onRowsChanged: (rows, rowByRowId, reason) {
_databaseCallbacks?.onRowsChanged?.call(rows, rowByRowId, reason);
},
onRowsDeleted: (ids) {
_databaseCallbacks?.onRowsDeleted?.call(ids);
@@ -291,7 +271,7 @@ class DatabaseController {
}
void _listenOnGroupChanged() {
_groupListener.start(
groupListener.start(
onNumOfGroupsChanged: (result) {
result.fold(
(changeset) {
@@ -322,14 +302,11 @@ class DatabaseController {
}
void _listenOnLayoutChanged() {
_layoutListener.start(
layoutListener.start(
onLayoutChanged: (result) {
result.fold(
(newDatabaseLayoutSetting) {
databaseLayoutSetting = newDatabaseLayoutSetting;
databaseLayoutSetting?.freeze();
_layoutCallbacks?.onLayoutChanged(newDatabaseLayoutSetting);
(l) {
_layoutCallbacks?.onLayoutChanged(l);
},
(r) => Log.error(r),
);
@@ -338,7 +315,7 @@ class DatabaseController {
}
void _listenOnCalendarLayoutChanged() {
_calendarLayoutListener.start(
calendarLayoutListener.start(
onCalendarLayoutChanged: (result) {
result.fold(
(l) {
@@ -364,15 +341,10 @@ class RowDataBuilder {
_cellDataByFieldId[fieldInfo.field.id] = num.toString();
}
/// The date should use the UTC timezone. Becuase the backend uses UTC timezone to format the time string.
void insertDate(FieldInfo fieldInfo, DateTime date) {
assert(
[
FieldType.DateTime,
FieldType.LastEditedTime,
FieldType.CreatedTime,
].contains(fieldInfo.fieldType),
);
final timestamp = date.millisecondsSinceEpoch ~/ 1000;
assert(fieldInfo.fieldType == FieldType.DateTime);
final timestamp = (date.toUtc().millisecondsSinceEpoch ~/ 1000);
_cellDataByFieldId[fieldInfo.field.id] = timestamp.toString();
}
@@ -1,5 +1,5 @@
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/database_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:dartz/dartz.dart';
@@ -1,15 +1,14 @@
import 'package:appflowy/plugins/database_view/application/row/row_service.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/calendar_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/group_changeset.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/calendar_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/database_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/group_changeset.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/setting_entities.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/group.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/row_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/group.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
class DatabaseViewBackendService {
final String viewId;
@@ -17,26 +16,22 @@ class DatabaseViewBackendService {
required this.viewId,
});
/// Returns the datbaase id associated with the view.
Future<Either<String, FlowyError>> getDatabaseId() async {
final payload = DatabaseViewIdPB(value: viewId);
return DatabaseEventGetDatabaseId(payload)
.send()
.then((value) => value.leftMap((l) => l.value));
}
Future<Either<DatabasePB, FlowyError>> openGrid() async {
await FolderEventSetLatestView(ViewIdPB(value: viewId)).send();
final payload = DatabaseViewIdPB(value: viewId);
return DatabaseEventGetDatabase(payload).send();
}
Future<Either<RowPB, FlowyError>> createRow({
RowId? startRowId,
String? startRowId,
String? groupId,
Map<String, String>? cellDataByFieldId,
}) {
final payload = CreateRowPayloadPB.create()..viewId = viewId;
payload.startRowId = startRowId ?? "";
var payload = CreateRowPayloadPB.create()..viewId = viewId;
if (startRowId != null) {
payload.startRowId = startRowId;
}
if (groupId != null) {
payload.groupId = groupId;
@@ -49,12 +44,12 @@ class DatabaseViewBackendService {
return DatabaseEventCreateRow(payload).send();
}
Future<Either<Unit, FlowyError>> moveGroupRow({
required RowId fromRowId,
Future<Either<Unit, FlowyError>> moveRow({
required String fromRowId,
required String toGroupId,
RowId? toRowId,
String? toRowId,
}) {
final payload = MoveGroupRowPayloadPB.create()
var payload = MoveGroupRowPayloadPB.create()
..viewId = viewId
..fromRowId = fromRowId
..toGroupId = toGroupId;
@@ -66,18 +61,6 @@ class DatabaseViewBackendService {
return DatabaseEventMoveGroupRow(payload).send();
}
Future<Either<Unit, FlowyError>> moveRow({
required String fromRowId,
required String toRowId,
}) {
final payload = MoveRowPayloadPB.create()
..viewId = viewId
..fromRowId = fromRowId
..toRowId = toRowId;
return DatabaseEventMoveRow(payload).send();
}
Future<Either<Unit, FlowyError>> moveGroup({
required String fromGroupId,
required String toGroupId,
@@ -93,7 +76,7 @@ class DatabaseViewBackendService {
Future<Either<List<FieldPB>, FlowyError>> getFields({
List<FieldIdPB>? fieldIds,
}) {
final payload = GetFieldPayloadPB.create()..viewId = viewId;
var payload = GetFieldPayloadPB.create()..viewId = viewId;
if (fieldIds != null) {
payload.fieldIds = RepeatedFieldIdPB(items: fieldIds);
@@ -103,23 +86,27 @@ class DatabaseViewBackendService {
});
}
Future<Either<DatabaseLayoutSettingPB, FlowyError>> getLayoutSetting(
DatabaseLayoutPB layoutType,
Future<Either<LayoutSettingPB, FlowyError>> getLayoutSetting(
LayoutTypePB layoutType,
) {
final payload = DatabaseLayoutMetaPB.create()
final payload = DatabaseLayoutIdPB.create()
..viewId = viewId
..layout = layoutType;
return DatabaseEventGetLayoutSetting(payload).send();
}
Future<Either<Unit, FlowyError>> updateLayoutSetting({
CalendarLayoutSettingPB? calendarLayoutSetting,
CalendarLayoutSettingsPB? calendarLayoutSetting,
}) {
final payload = LayoutSettingChangesetPB.create()..viewId = viewId;
final layoutSetting = LayoutSettingPB.create();
if (calendarLayoutSetting != null) {
payload.calendar = calendarLayoutSetting;
layoutSetting.calendar = calendarLayoutSetting;
}
final payload = UpdateLayoutSettingPB.create()
..viewId = viewId
..layoutSetting = layoutSetting;
return DatabaseEventSetLayoutSetting(payload).send();
}
@@ -1,23 +1,22 @@
import 'dart:collection';
import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/database_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import '../grid/presentation/widgets/filter/filter_info.dart';
import 'field/field_controller.dart';
import 'row/row_cache.dart';
import 'row/row_service.dart';
typedef OnFieldsChanged = void Function(UnmodifiableListView<FieldInfo>);
typedef OnFiltersChanged = void Function(List<FilterInfo>);
typedef OnDatabaseChanged = void Function(DatabasePB);
typedef OnRowsCreated = void Function(List<RowId> ids);
typedef OnRowsUpdated = void Function(List<RowId> ids);
typedef OnRowsDeleted = void Function(List<RowId> ids);
typedef OnNumOfRowsChanged = void Function(
typedef OnRowsCreated = void Function(List<String> ids);
typedef OnRowsUpdated = void Function(List<String> ids);
typedef OnRowsDeleted = void Function(List<String> ids);
typedef OnRowsChanged = void Function(
UnmodifiableListView<RowInfo> rows,
UnmodifiableMapView<RowId, RowInfo> rowByRowId,
UnmodifiableMapView<String, RowInfo> rowByRowId,
RowsChangedReason reason,
);
@@ -1,5 +1,5 @@
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'field_service.dart';
@@ -10,7 +10,7 @@ class FieldActionSheetBloc
extends Bloc<FieldActionSheetEvent, FieldActionSheetState> {
final FieldBackendService fieldService;
FieldActionSheetBloc({required FieldContext fieldCellContext})
FieldActionSheetBloc({required FieldCellContext fieldCellContext})
: fieldService = FieldBackendService(
viewId: fieldCellContext.viewId,
fieldId: fieldCellContext.field.id,
@@ -1,5 +1,5 @@
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
@@ -14,7 +14,7 @@ class FieldCellBloc extends Bloc<FieldCellEvent, FieldCellState> {
final FieldBackendService _fieldBackendSvc;
FieldCellBloc({
required FieldContext cellContext,
required FieldCellContext cellContext,
}) : _fieldListener = SingleFieldListener(fieldId: cellContext.field.id),
_fieldBackendSvc = FieldBackendService(
viewId: cellContext.viewId,
@@ -83,7 +83,8 @@ class FieldCellState with _$FieldCellState {
required double width,
}) = _FieldCellState;
factory FieldCellState.initial(FieldContext cellContext) => FieldCellState(
factory FieldCellState.initial(FieldCellContext cellContext) =>
FieldCellState(
viewId: cellContext.viewId,
field: cellContext.field,
width: cellContext.field.width.toDouble(),
@@ -1,13 +1,13 @@
import 'dart:collection';
import 'package:appflowy_backend/protobuf/flowy-database2/filter_changeset.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/sort_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/filter_changeset.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/sort_entities.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/group.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/util.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/group.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/setting_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/util.pb.dart';
import 'package:flutter/foundation.dart';
import '../../grid/presentation/widgets/filter/filter_info.dart';
import '../../grid/presentation/widgets/sort/sort_info.dart';
@@ -94,7 +94,7 @@ class FieldController {
_updatedFieldCallbacks = {};
// Group callbacks
final Map<String, GroupSettingPB> _groupConfigurationByFieldId = {};
final Map<String, GroupConfigurationPB> _groupConfigurationByFieldId = {};
// Filter callbacks
final Map<OnReceiveFilters, VoidCallback> _filterCallbacks = {};
@@ -401,7 +401,7 @@ class FieldController {
void _updateSetting(DatabaseViewSettingPB setting) {
_groupConfigurationByFieldId.clear();
for (final configuration in setting.groupSettings.items) {
for (final configuration in setting.groupConfigurations.items) {
_groupConfigurationByFieldId[configuration.fieldId] = configuration;
}
@@ -418,7 +418,7 @@ class FieldController {
void _updateFieldInfos() {
if (_fieldNotifier != null) {
for (final field in _fieldNotifier!.fieldInfos) {
for (var field in _fieldNotifier!.fieldInfos) {
field._isGroupField = _groupConfigurationByFieldId[field.id] != null;
field._hasFilter = _filterPBByFieldId[field.id] != null;
field._hasSort = _sortPBByFieldId[field.id] != null;
@@ -1,4 +1,4 @@
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:dartz/dartz.dart';
import 'field_service.dart';
@@ -12,20 +12,12 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
final TypeOptionController dataController;
FieldEditorBloc({
required String viewId,
required String fieldName,
required bool isGroupField,
required FieldPB field,
required FieldTypeOptionLoader loader,
}) : dataController = TypeOptionController(
field: field,
loader: loader,
),
super(
FieldEditorState.initial(
loader.viewId,
loader.field.name,
isGroupField,
),
) {
required IFieldTypeOptionLoader loader,
}) : dataController = TypeOptionController(viewId: viewId, loader: loader),
super(FieldEditorState.initial(viewId, fieldName, isGroupField)) {
on<FieldEditorEvent>(
(event, emit) async {
await event.when(
@@ -35,8 +27,7 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
add(FieldEditorEvent.didReceiveFieldChanged(field));
}
});
await dataController.reloadTypeOption();
add(FieldEditorEvent.didReceiveFieldChanged(dataController.field));
await dataController.loadTypeOptionData();
},
updateName: (name) {
if (state.name != name) {
@@ -58,7 +49,7 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
() => null,
(field) {
final fieldService = FieldBackendService(
viewId: loader.viewId,
viewId: viewId,
fieldId: field.id,
);
fieldService.deleteField();
@@ -1,11 +1,11 @@
import 'package:appflowy/core/notification/grid_notification.dart';
import 'package:appflowy/core/grid_notification.dart';
import 'package:dartz/dartz.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/notification.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/notification.pb.dart';
import 'package:flowy_infra/notifier.dart';
import 'dart:async';
import 'dart:typed_data';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
typedef UpdateFieldNotifiedValue = Either<FieldPB, FlowyError>;
@@ -1,8 +1,8 @@
import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/database_entities.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'field_service.freezed.dart';
@@ -29,11 +29,12 @@ class FieldBackendService {
Future<Either<Unit, FlowyError>> updateField({
String? name,
FieldType? fieldType,
bool? frozen,
bool? visibility,
double? width,
}) {
final payload = FieldChangesetPB.create()
var payload = FieldChangesetPB.create()
..viewId = viewId
..fieldId = fieldId;
@@ -41,6 +42,10 @@ class FieldBackendService {
payload.name = name;
}
if (fieldType != null) {
payload.fieldType = fieldType;
}
if (frozen != null) {
payload.frozen = frozen;
}
@@ -61,7 +66,7 @@ class FieldBackendService {
required String fieldId,
required List<int> typeOptionData,
}) {
final payload = TypeOptionChangesetPB.create()
var payload = TypeOptionChangesetPB.create()
..viewId = viewId
..fieldId = fieldId
..typeOptionData = typeOptionData;
@@ -102,8 +107,8 @@ class FieldBackendService {
}
@freezed
class FieldContext with _$FieldContext {
const factory FieldContext({
class FieldCellContext with _$FieldCellContext {
const factory FieldCellContext({
required String viewId,
required FieldPB field,
}) = _FieldCellContext;
@@ -1,4 +1,4 @@
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
@@ -1,4 +1,5 @@
import 'package:appflowy_backend/protobuf/flowy-database2/date_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/date_type_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/date_type_option_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:protobuf/protobuf.dart';
@@ -40,8 +41,8 @@ class DateTypeOptionBloc
}
DateTypeOptionPB _updateTypeOption({
DateFormatPB? dateFormat,
TimeFormatPB? timeFormat,
DateFormat? dateFormat,
TimeFormat? timeFormat,
bool? includeTime,
}) {
state.typeOption.freeze();
@@ -53,15 +54,19 @@ class DateTypeOptionBloc
if (timeFormat != null) {
typeOption.timeFormat = timeFormat;
}
if (includeTime != null) {
typeOption.includeTime = includeTime;
}
});
}
}
@freezed
class DateTypeOptionEvent with _$DateTypeOptionEvent {
const factory DateTypeOptionEvent.didSelectDateFormat(DateFormatPB format) =
const factory DateTypeOptionEvent.didSelectDateFormat(DateFormat format) =
_DidSelectDateFormat;
const factory DateTypeOptionEvent.didSelectTimeFormat(TimeFormatPB format) =
const factory DateTypeOptionEvent.didSelectTimeFormat(TimeFormat format) =
_DidSelectTimeFormat;
const factory DateTypeOptionEvent.includeTime(bool includeTime) =
_IncludeTime;
@@ -1,4 +1,4 @@
import 'package:appflowy_backend/protobuf/flowy-database2/select_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:protobuf/protobuf.dart';
@@ -1,5 +1,6 @@
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/select_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/multi_select_type_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
import 'dart:async';
import 'select_option_type_option_bloc.dart';
import 'type_option_context.dart';
@@ -1,4 +1,5 @@
import 'package:appflowy_backend/protobuf/flowy-database2/number_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/format.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-database/number_type_option.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:protobuf/protobuf.dart';
@@ -21,7 +22,7 @@ class NumberTypeOptionBloc
);
}
NumberTypeOptionPB _updateNumberFormat(NumberFormatPB format) {
NumberTypeOptionPB _updateNumberFormat(NumberFormat format) {
state.typeOption.freeze();
return state.typeOption.rebuild((typeOption) {
typeOption.format = format;
@@ -31,7 +32,7 @@ class NumberTypeOptionBloc
@freezed
class NumberTypeOptionEvent with _$NumberTypeOptionEvent {
const factory NumberTypeOptionEvent.didSelectFormat(NumberFormatPB format) =
const factory NumberTypeOptionEvent.didSelectFormat(NumberFormat format) =
_DidSelectFormat;
}
@@ -1,4 +1,4 @@
import 'package:appflowy_backend/protobuf/flowy-database2/number_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-database/format.pbenum.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'number_format_bloc.freezed.dart';
@@ -9,8 +9,7 @@ class NumberFormatBloc extends Bloc<NumberFormatEvent, NumberFormatState> {
(event, emit) async {
event.map(
setFilter: (_SetFilter value) {
final List<NumberFormatPB> formats =
List.from(NumberFormatPB.values);
final List<NumberFormat> formats = List.from(NumberFormat.values);
if (value.filter.isNotEmpty) {
formats.retainWhere(
(element) => element
@@ -35,92 +34,92 @@ class NumberFormatEvent with _$NumberFormatEvent {
@freezed
class NumberFormatState with _$NumberFormatState {
const factory NumberFormatState({
required List<NumberFormatPB> formats,
required List<NumberFormat> formats,
required String filter,
}) = _NumberFormatState;
factory NumberFormatState.initial() {
return const NumberFormatState(
formats: NumberFormatPB.values,
formats: NumberFormat.values,
filter: "",
);
}
}
extension NumberFormatExtension on NumberFormatPB {
extension NumberFormatExtension on NumberFormat {
String title() {
switch (this) {
case NumberFormatPB.ArgentinePeso:
case NumberFormat.ArgentinePeso:
return "Argentine peso";
case NumberFormatPB.Baht:
case NumberFormat.Baht:
return "Baht";
case NumberFormatPB.CanadianDollar:
case NumberFormat.CanadianDollar:
return "Canadian dollar";
case NumberFormatPB.ChileanPeso:
case NumberFormat.ChileanPeso:
return "Chilean peso";
case NumberFormatPB.ColombianPeso:
case NumberFormat.ColombianPeso:
return "Colombian peso";
case NumberFormatPB.DanishKrone:
case NumberFormat.DanishKrone:
return "Danish krone";
case NumberFormatPB.Dirham:
case NumberFormat.Dirham:
return "Dirham";
case NumberFormatPB.EUR:
case NumberFormat.EUR:
return "Euro";
case NumberFormatPB.Forint:
case NumberFormat.Forint:
return "Forint";
case NumberFormatPB.Franc:
case NumberFormat.Franc:
return "Franc";
case NumberFormatPB.HongKongDollar:
case NumberFormat.HongKongDollar:
return "Hone Kong dollar";
case NumberFormatPB.Koruna:
case NumberFormat.Koruna:
return "Koruna";
case NumberFormatPB.Krona:
case NumberFormat.Krona:
return "Krona";
case NumberFormatPB.Leu:
case NumberFormat.Leu:
return "Leu";
case NumberFormatPB.Lira:
case NumberFormat.Lira:
return "Lira";
case NumberFormatPB.MexicanPeso:
case NumberFormat.MexicanPeso:
return "Mexican peso";
case NumberFormatPB.NewTaiwanDollar:
case NumberFormat.NewTaiwanDollar:
return "New Taiwan dollar";
case NumberFormatPB.NewZealandDollar:
case NumberFormat.NewZealandDollar:
return "New Zealand dollar";
case NumberFormatPB.NorwegianKrone:
case NumberFormat.NorwegianKrone:
return "Norwegian krone";
case NumberFormatPB.Num:
case NumberFormat.Num:
return "Number";
case NumberFormatPB.Percent:
case NumberFormat.Percent:
return "Percent";
case NumberFormatPB.PhilippinePeso:
case NumberFormat.PhilippinePeso:
return "Philippine peso";
case NumberFormatPB.Pound:
case NumberFormat.Pound:
return "Pound";
case NumberFormatPB.Rand:
case NumberFormat.Rand:
return "Rand";
case NumberFormatPB.Real:
case NumberFormat.Real:
return "Real";
case NumberFormatPB.Ringgit:
case NumberFormat.Ringgit:
return "Ringgit";
case NumberFormatPB.Riyal:
case NumberFormat.Riyal:
return "Riyal";
case NumberFormatPB.Ruble:
case NumberFormat.Ruble:
return "Ruble";
case NumberFormatPB.Rupee:
case NumberFormat.Rupee:
return "Rupee";
case NumberFormatPB.Rupiah:
case NumberFormat.Rupiah:
return "Rupiah";
case NumberFormatPB.Shekel:
case NumberFormat.Shekel:
return "Skekel";
case NumberFormatPB.USD:
case NumberFormat.USD:
return "US dollar";
case NumberFormatPB.UruguayanPeso:
case NumberFormat.UruguayanPeso:
return "Uruguayan peso";
case NumberFormatPB.Won:
case NumberFormat.Won:
return "Won";
case NumberFormatPB.Yen:
case NumberFormat.Yen:
return "Yen";
case NumberFormatPB.Yuan:
case NumberFormat.Yuan:
return "Yuan";
default:
throw UnimplementedError;
@@ -129,13 +128,13 @@ extension NumberFormatExtension on NumberFormatPB {
// String iconName() {
// switch (this) {
// case NumberFormatPB.CNY:
// case NumberFormat.CNY:
// return "grid/field/yen";
// case NumberFormatPB.EUR:
// case NumberFormat.EUR:
// return "grid/field/euro";
// case NumberFormatPB.Number:
// case NumberFormat.Number:
// return "grid/field/numbers";
// case NumberFormatPB.USD:
// case NumberFormat.USD:
// return "grid/field/us_dollar";
// default:
// throw UnimplementedError;
@@ -1,11 +1,11 @@
import 'package:appflowy_backend/protobuf/flowy-database2/select_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
import 'package:dartz/dartz.dart';
part 'select_option_type_option_bloc.freezed.dart';
abstract mixin class ISelectOptionAction {
abstract class ISelectOptionAction {
Future<List<SelectOptionPB>> Function(String) get insertOption;
List<SelectOptionPB> Function(SelectOptionPB) get deleteOption;
@@ -1,5 +1,6 @@
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/select_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/single_select_type_option.pb.dart';
import 'dart:async';
import 'package:protobuf/protobuf.dart';
import 'select_option_type_option_bloc.dart';
@@ -1,14 +1,17 @@
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/checkbox_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/date_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/number_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/select_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/text_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/url_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/checkbox_type_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/checklist_type_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/date_type_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:appflowy_backend/protobuf/flowy-database/multi_select_type_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/number_type_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/single_select_type_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/text_type_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/url_type_option.pb.dart';
import 'package:protobuf/protobuf.dart';
import 'type_option_data_controller.dart';
abstract class TypeOptionParser<T> {
@@ -113,7 +116,7 @@ class TypeOptionContext<T extends GeneratedMessage> {
required TypeOptionController dataController,
}) : _dataController = dataController;
String get viewId => _dataController.loader.viewId;
String get viewId => _dataController.viewId;
String get fieldId => _dataController.field.id;
@@ -121,7 +124,7 @@ class TypeOptionContext<T extends GeneratedMessage> {
void Function(T)? onCompleted,
required void Function(FlowyError) onError,
}) async {
await _dataController.reloadTypeOption().then((result) {
await _dataController.loadTypeOptionData().then((result) {
result.fold((l) => null, (err) => onError(err));
});
@@ -150,15 +153,65 @@ abstract class TypeOptionFieldDelegate {
void dispose();
}
abstract class ITypeOptionLoader {
abstract class IFieldTypeOptionLoader {
String get viewId;
String get fieldName;
Future<Either<TypeOptionPB, FlowyError>> load();
Future<Either<TypeOptionPB, FlowyError>> initialize();
Future<Either<Unit, FlowyError>> switchToField(
String fieldId,
FieldType fieldType,
) {
final payload = UpdateFieldTypePayloadPB.create()
..viewId = viewId
..fieldId = fieldId
..fieldType = fieldType;
return DatabaseEventUpdateFieldType(payload).send();
}
}
/// Uses when creating a new field
class NewFieldTypeOptionLoader extends IFieldTypeOptionLoader {
TypeOptionPB? fieldTypeOption;
@override
final String viewId;
NewFieldTypeOptionLoader({
required this.viewId,
});
/// Creates the field type option if the fieldTypeOption is null.
/// Otherwise, it loads the type option data from the backend.
@override
Future<Either<TypeOptionPB, FlowyError>> load() {
if (fieldTypeOption != null) {
final payload = TypeOptionPathPB.create()
..viewId = viewId
..fieldId = fieldTypeOption!.field_2.id
..fieldType = fieldTypeOption!.field_2.fieldType;
return DatabaseEventGetTypeOption(payload).send();
} else {
final payload = CreateFieldPayloadPB.create()
..viewId = viewId
..fieldType = FieldType.RichText;
return DatabaseEventCreateTypeOption(payload).send().then((result) {
return result.fold(
(newFieldTypeOption) {
fieldTypeOption = newFieldTypeOption;
return left(newFieldTypeOption);
},
(err) => right(err),
);
});
}
}
}
/// Uses when editing a existing field
class FieldTypeOptionLoader {
class FieldTypeOptionLoader extends IFieldTypeOptionLoader {
@override
final String viewId;
final FieldPB field;
@@ -167,6 +220,7 @@ class FieldTypeOptionLoader {
required this.field,
});
@override
Future<Either<TypeOptionPB, FlowyError>> load() {
final payload = TypeOptionPathPB.create()
..viewId = viewId
@@ -1,8 +1,7 @@
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:flowy_infra/notifier.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:protobuf/protobuf.dart' hide FieldInfo;
import 'package:appflowy_backend/log.dart';
@@ -11,26 +10,30 @@ import '../field_service.dart';
import 'type_option_context.dart';
class TypeOptionController {
final String viewId;
late TypeOptionPB _typeOption;
final FieldTypeOptionLoader loader;
final IFieldTypeOptionLoader loader;
final PublishNotifier<FieldPB> _fieldNotifier = PublishNotifier();
/// Returns a [TypeOptionController] used to modify the specified
/// [FieldPB]'s data
///
/// Should call [reloadTypeOption] if the passed-in [FieldInfo]
/// Should call [loadTypeOptionData] if the passed-in [FieldInfo]
/// is null
///
TypeOptionController({
required this.viewId,
required this.loader,
required FieldPB field,
FieldInfo? fieldInfo,
}) {
_typeOption = TypeOptionPB.create()
..viewId = loader.viewId
..field_2 = field;
if (fieldInfo != null) {
_typeOption = TypeOptionPB.create()
..viewId = viewId
..field_2 = fieldInfo.field;
}
}
Future<Either<TypeOptionPB, FlowyError>> reloadTypeOption() async {
Future<Either<TypeOptionPB, FlowyError>> loadTypeOptionData() async {
final result = await loader.load();
return result.fold(
(data) {
@@ -63,7 +66,7 @@ class TypeOptionController {
_fieldNotifier.value = _typeOption.field_2;
FieldBackendService(viewId: loader.viewId, fieldId: field.id)
FieldBackendService(viewId: viewId, fieldId: field.id)
.updateField(name: name);
}
@@ -75,25 +78,20 @@ class TypeOptionController {
});
FieldBackendService.updateFieldTypeOption(
viewId: loader.viewId,
viewId: viewId,
fieldId: field.id,
typeOptionData: typeOptionData,
);
}
Future<void> switchToField(FieldType newFieldType) async {
final payload = UpdateFieldTypePayloadPB.create()
..viewId = loader.viewId
..fieldId = field.id
..fieldType = newFieldType;
final result = await DatabaseEventUpdateFieldType(payload).send();
final result = await loader.switchToField(field.id, newFieldType);
await result.fold(
(_) {
// Should load the type-option data after switching to a new field.
// After loading the type-option data, the editor widget that uses
// the type-option data will be rebuild.
reloadTypeOption();
loadTypeOptionData();
},
(err) => Future(() => Log.error(err)),
);
@@ -1,7 +1,8 @@
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:dartz/dartz.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/cell_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
class TypeOptionBackendService {
final String viewId;
@@ -22,15 +23,4 @@ class TypeOptionBackendService {
return DatabaseEventCreateSelectOption(payload).send();
}
static Future<Either<TypeOptionPB, FlowyError>> createFieldTypeOption({
required String viewId,
FieldType fieldType = FieldType.RichText,
}) {
final payload = CreateFieldPayloadPB.create()
..viewId = viewId
..fieldType = FieldType.RichText;
return DatabaseEventCreateTypeOption(payload).send();
}
}
@@ -1,12 +1,12 @@
import 'dart:typed_data';
import 'package:appflowy/core/notification/grid_notification.dart';
import 'package:appflowy/core/grid_notification.dart';
import 'package:flowy_infra/notifier.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/notification.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/filter_changeset.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/notification.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/filter_changeset.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/util.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/util.pb.dart';
typedef UpdateFilterNotifiedValue
= Either<FilterChangesetNotificationPB, FlowyError>;
@@ -1,17 +1,17 @@
import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/database_entities.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/checkbox_filter.pbserver.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/checklist_filter.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/date_filter.pbserver.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/number_filter.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/select_option_filter.pbserver.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/text_filter.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/util.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/checkbox_filter.pbserver.dart';
import 'package:appflowy_backend/protobuf/flowy-database/checklist_filter.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/date_filter.pbserver.dart';
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/number_filter.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/select_option_filter.pbserver.dart';
import 'package:appflowy_backend/protobuf/flowy-database/setting_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/text_filter.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/util.pb.dart';
import 'package:fixnum/fixnum.dart' as $fixnum;
class FilterBackendService {
@@ -84,20 +84,11 @@ class FilterBackendService {
required String fieldId,
String? filterId,
required DateFilterConditionPB condition,
required FieldType fieldType,
int? start,
int? end,
int? timestamp,
}) {
assert(
[
FieldType.DateTime,
FieldType.LastEditedTime,
FieldType.CreatedTime,
].contains(fieldType),
);
final filter = DateFilterPB();
var filter = DateFilterPB();
if (timestamp != null) {
filter.timestamp = $fixnum.Int64(timestamp);
} else {
@@ -114,7 +105,7 @@ class FilterBackendService {
return insertFilter(
fieldId: fieldId,
filterId: filterId,
fieldType: fieldType,
fieldType: FieldType.DateTime,
data: filter.writeToBuffer(),
);
}
@@ -178,7 +169,7 @@ class FilterBackendService {
required FieldType fieldType,
required List<int> data,
}) {
final insertFilterPayload = UpdateFilterPayloadPB.create()
var insertFilterPayload = AlterFilterPayloadPB.create()
..fieldId = fieldId
..fieldType = fieldType
..viewId = viewId
@@ -190,7 +181,7 @@ class FilterBackendService {
final payload = DatabaseSettingChangesetPB.create()
..viewId = viewId
..updateFilter = insertFilterPayload;
..alterFilter = insertFilterPayload;
return DatabaseEventUpdateDatabaseSetting(payload).send().then((result) {
return result.fold(
(l) => left(l),
@@ -1,14 +1,14 @@
import 'dart:typed_data';
import 'package:appflowy/core/notification/grid_notification.dart';
import 'package:appflowy/core/grid_notification.dart';
import 'package:flowy_infra/notifier.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/notification.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/notification.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/group.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/group_changeset.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/group.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/group_changeset.pb.dart';
typedef GroupUpdateValue = Either<GroupChangesPB, FlowyError>;
typedef GroupUpdateValue = Either<GroupChangesetPB, FlowyError>;
typedef GroupByNewFieldValue = Either<List<GroupPB>, FlowyError>;
class DatabaseGroupListener {
@@ -36,17 +36,17 @@ class DatabaseGroupListener {
Either<Uint8List, FlowyError> result,
) {
switch (ty) {
case DatabaseNotification.DidUpdateNumOfGroups:
case DatabaseNotification.DidUpdateGroups:
result.fold(
(payload) => _numOfGroupsNotifier?.value =
left(GroupChangesPB.fromBuffer(payload)),
left(GroupChangesetPB.fromBuffer(payload)),
(error) => _numOfGroupsNotifier?.value = right(error),
);
break;
case DatabaseNotification.DidGroupByField:
result.fold(
(payload) => _groupByFieldNotifier?.value =
left(GroupChangesPB.fromBuffer(payload).initialGroups),
left(GroupChangesetPB.fromBuffer(payload).initialGroups),
(error) => _groupByFieldNotifier?.value = right(error),
);
break;
@@ -1,34 +0,0 @@
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/group.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:dartz/dartz.dart';
class GroupBackendService {
final String viewId;
GroupBackendService(this.viewId);
Future<Either<Unit, FlowyError>> groupByField({
required String fieldId,
}) {
final payload = GroupByFieldPayloadPB.create()
..viewId = viewId
..fieldId = fieldId;
return DatabaseEventSetGroupByField(payload).send();
}
Future<Either<Unit, FlowyError>> updateGroup({
required String groupId,
String? name,
bool? visible,
}) {
final payload = UpdateGroupPB.create()..groupId = groupId;
if (name != null) {
payload.name = name;
}
if (visible != null) {
payload.visible = visible;
}
return DatabaseEventUpdateGroup(payload).send();
}
}
@@ -1,12 +1,12 @@
import 'dart:typed_data';
import 'package:appflowy/core/notification/grid_notification.dart';
import 'package:appflowy/core/grid_notification.dart';
import 'package:flowy_infra/notifier.dart';
import 'package:appflowy_backend/protobuf/flowy-error/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
import 'package:dartz/dartz.dart';
typedef NewLayoutFieldValue = Either<DatabaseLayoutSettingPB, FlowyError>;
typedef NewLayoutFieldValue = Either<LayoutSettingPB, FlowyError>;
class DatabaseCalendarLayoutListener {
final String viewId;
@@ -33,7 +33,7 @@ class DatabaseCalendarLayoutListener {
case DatabaseNotification.DidSetNewLayoutField:
result.fold(
(payload) => _newLayoutFieldNotifier?.value =
left(DatabaseLayoutSettingPB.fromBuffer(payload)),
left(LayoutSettingPB.fromBuffer(payload)),
(error) => _newLayoutFieldNotifier?.value = right(error),
);
break;
Loaded 100 of 1523 files, more files were not shown because too many files have changed in this diff. Show more