mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: Create a "view" for all database references in a document (#2083)
* feat: add archive for compression * feat: add service to manage zipped work spaces * feat: export service in barrel file * feat: ignore .ephemeral directory * feat: add first compressed workspace file * fix: directory path was wrong * feat: add a somewhat useful test * fix: move to same file (delete later) * fix: use script path vs. working directory for CI * fix: read from asset bundle instead of file system * fix: workaround to run integration in multiple files on desktop (flutter/flutter#101031 * feat: remove .ephemeral from .gitignore, no longer created * feat: document test changes * fix: lucas suggestion * feat: mark assets as excluded in pubspec.yaml * feat: add class for build utilities * feat: add script runner for release builds * feat: add build script as task in flowy project * fix: typo in pubspec.yaml * chore: use constants for exclude tag * feat: add appversion as argument to build tool * feat: use dart script in release.yml * chore: remove task * fix: careless error Co-authored-by: Mihir <84044317+squidrye@users.noreply.github.com> * feat: add translations for view of * fix: typo in getAllDatabase * feat: add view of database * fix: remove unused import * fix: use effective dart typing * fix: insertPage marked as async, should return future * fix: Remove multi-line string * fix: ref can be null * fix: unused imports caused analyzer to fail * feat: also fix. Add empty document as option and change name to _name * chore: move referenced database tests to empty document test file * feat: add test utilities * feat: add new integration test on an empty document * feat: register test in runner * fix: missing reference in insert_page_command * fix: analyzer errors --------- Co-authored-by: Mihir <84044317+squidrye@users.noreply.github.com>
This commit is contained in:
parent
231fd38298
commit
e2009c063b
7
.github/workflows/integration_test.yml
vendored
7
.github/workflows/integration_test.yml
vendored
@ -105,11 +105,11 @@ jobs:
|
|||||||
working-directory: frontend/appflowy_flutter
|
working-directory: frontend/appflowy_flutter
|
||||||
run: |
|
run: |
|
||||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||||
flutter test integration_test -d Linux --coverage
|
flutter test integration_test/runner.dart -d Linux --coverage
|
||||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||||
flutter test integration_test -d macOS --coverage
|
flutter test integration_test/runner.dart -d macOS --coverage
|
||||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||||
flutter test integration_test -d Windows --coverage
|
flutter test integration_test/runner.dart -d Windows --coverage
|
||||||
fi
|
fi
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
@ -120,4 +120,3 @@ jobs:
|
|||||||
# env_vars: ${{ matrix.os }}
|
# env_vars: ${{ matrix.os }}
|
||||||
# fail_ci_if_error: true
|
# fail_ci_if_error: true
|
||||||
# verbose: true
|
# verbose: true
|
||||||
|
|
||||||
|
30
.github/workflows/release.yml
vendored
30
.github/workflows/release.yml
vendored
@ -3,7 +3,7 @@ name: release
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- '*'
|
- "*"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
FLUTTER_VERSION: "3.7.5"
|
FLUTTER_VERSION: "3.7.5"
|
||||||
@ -136,7 +136,11 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
job:
|
job:
|
||||||
- { target: x86_64-apple-darwin, os: macos-10.15, extra-build-args: "" }
|
- {
|
||||||
|
target: x86_64-apple-darwin,
|
||||||
|
os: macos-10.15,
|
||||||
|
extra-build-args: "",
|
||||||
|
}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@ -172,7 +176,7 @@ jobs:
|
|||||||
working-directory: frontend
|
working-directory: frontend
|
||||||
run: |
|
run: |
|
||||||
flutter config --enable-macos-desktop
|
flutter config --enable-macos-desktop
|
||||||
cargo make --env APP_VERSION=${{ github.ref_name }} --profile production-mac-x86_64 appflowy
|
dart ./scripts/flutter_release_build/build_flowy.dart . ${{ github.ref_name }}
|
||||||
|
|
||||||
- name: Create macOS dmg
|
- name: Create macOS dmg
|
||||||
run: |
|
run: |
|
||||||
@ -225,9 +229,21 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
job:
|
job:
|
||||||
- { arch: x86_64, target: x86_64-unknown-linux-gnu, os: ubuntu-20.04, extra-build-args: "", flutter_profile: production-linux-x86_64 }
|
- {
|
||||||
|
arch: x86_64,
|
||||||
|
target: x86_64-unknown-linux-gnu,
|
||||||
|
os: ubuntu-20.04,
|
||||||
|
extra-build-args: "",
|
||||||
|
flutter_profile: production-linux-x86_64,
|
||||||
|
}
|
||||||
# - { arch: aarch64, target: aarch64-unknown-linux-gnu, os: ubuntu-20.04, extra-build-args: "", flutter_profile: production-linux-aarch64 }
|
# - { arch: aarch64, target: aarch64-unknown-linux-gnu, os: ubuntu-20.04, extra-build-args: "", flutter_profile: production-linux-aarch64 }
|
||||||
- { arch: x86_64, target: x86_64-unknown-linux-gnu, os: ubuntu-18.04, extra-build-args: "", flutter_profile: production-linux-x86_64}
|
- {
|
||||||
|
arch: x86_64,
|
||||||
|
target: x86_64-unknown-linux-gnu,
|
||||||
|
os: ubuntu-18.04,
|
||||||
|
extra-build-args: "",
|
||||||
|
flutter_profile: production-linux-x86_64,
|
||||||
|
}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@ -275,7 +291,7 @@ jobs:
|
|||||||
working-directory: frontend
|
working-directory: frontend
|
||||||
run: |
|
run: |
|
||||||
flutter config --enable-linux-desktop
|
flutter config --enable-linux-desktop
|
||||||
cargo make --env APP_VERSION=${{ github.ref_name }} --profile ${{ matrix.job.flutter_profile}} appflowy
|
dart ./scripts/flutter_release_build/build_flowy.dart . ${{ github.ref_name }}
|
||||||
|
|
||||||
- name: Archive Assert
|
- name: Archive Assert
|
||||||
working-directory: ${{ env.LINUX_APP_RELEASE_PATH }}
|
working-directory: ${{ env.LINUX_APP_RELEASE_PATH }}
|
||||||
@ -361,4 +377,4 @@ jobs:
|
|||||||
- name: Notify Discord
|
- name: Notify Discord
|
||||||
run: |
|
run: |
|
||||||
curl -H "Content-Type: application/json" -d '{"username": "release@appflowy", "content": "🎉 AppFlowy ${{ github.ref_name }} is available. https://github.com/AppFlowy-IO/AppFlowy/releases/tag/'${{ github.ref_name }}'"}' "https://discord.com/api/webhooks/${{ secrets.DISCORD }}"
|
curl -H "Content-Type: application/json" -d '{"username": "release@appflowy", "content": "🎉 AppFlowy ${{ github.ref_name }} is available. https://github.com/AppFlowy-IO/AppFlowy/releases/tag/'${{ github.ref_name }}'"}' "https://discord.com/api/webhooks/${{ secrets.DISCORD }}"
|
||||||
shell: bash
|
shell: bash
|
||||||
|
446
frontend/.vscode/tasks.json
vendored
446
frontend/.vscode/tasks.json
vendored
@ -1,223 +1,225 @@
|
|||||||
{
|
{
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
// https://code.visualstudio.com/docs/editor/tasks
|
// https://code.visualstudio.com/docs/editor/tasks
|
||||||
// https://gist.github.com/deadalusai/9e13e36d61ec7fb72148
|
// https://gist.github.com/deadalusai/9e13e36d61ec7fb72148
|
||||||
// ${workspaceRoot}: the root folder of the team
|
// ${workspaceRoot}: the root folder of the team
|
||||||
// ${file}: the current opened file
|
// ${file}: the current opened file
|
||||||
// ${fileBasename}: the current opened file's basename
|
// ${fileBasename}: the current opened file's basename
|
||||||
// ${fileDirname}: the current opened file's dirname
|
// ${fileDirname}: the current opened file's dirname
|
||||||
// ${fileExtname}: the current opened file's extension
|
// ${fileExtname}: the current opened file's extension
|
||||||
// ${cwd}: the current working directory of the spawned process
|
// ${cwd}: the current working directory of the spawned process
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"label": "AF: Clean + Rebuild All",
|
"label": "AF: Clean + Rebuild All",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"dependsOrder": "sequence",
|
"dependsOrder": "sequence",
|
||||||
"dependsOn": [
|
"dependsOn": [
|
||||||
"AF: Dart Clean",
|
"AF: Dart Clean",
|
||||||
"AF: Flutter Clean",
|
"AF: Flutter Clean",
|
||||||
"AF: Build Appflowy Core",
|
"AF: Build Appflowy Core",
|
||||||
"AF: Flutter Pub Get",
|
"AF: Flutter Pub Get",
|
||||||
"AF: Flutter Package Get",
|
"AF: Flutter Package Get",
|
||||||
"AF: Generate Language Files",
|
"AF: Generate Language Files",
|
||||||
"AF: Generate Freezed Files"
|
"AF: Generate Freezed Files"
|
||||||
],
|
],
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"reveal": "always",
|
"reveal": "always",
|
||||||
"panel": "new"
|
"panel": "new"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "AF: Clean + Rebuild All (Android)",
|
"label": "AF: Clean + Rebuild All (Android)",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"dependsOrder": "sequence",
|
"dependsOrder": "sequence",
|
||||||
"dependsOn": [
|
"dependsOn": [
|
||||||
"AF: Dart Clean",
|
"AF: Dart Clean",
|
||||||
"AF: Flutter Clean",
|
"AF: Flutter Clean",
|
||||||
"AF: Build Appflowy Core_for_android",
|
"AF: Build Appflowy Core_for_android",
|
||||||
"AF: Flutter Pub Get",
|
"AF: Flutter Pub Get",
|
||||||
"AF: Flutter Package Get",
|
"AF: Flutter Package Get",
|
||||||
"AF: Generate Language Files",
|
"AF: Generate Language Files",
|
||||||
"AF: Generate Freezed Files"
|
"AF: Generate Freezed Files"
|
||||||
],
|
],
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"reveal": "always",
|
"reveal": "always",
|
||||||
"panel": "new"
|
"panel": "new"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "AF: Build Appflowy Core_for_android",
|
"label": "AF: Build Appflowy Core_for_android",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "cargo make --profile development-android appflowy-core-dev-android",
|
"command": "cargo make --profile development-android appflowy-core-dev-android",
|
||||||
"group": "build",
|
"group": "build",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "AF: Build Appflowy Core",
|
"label": "AF: Build Appflowy Core",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"windows": {
|
"windows": {
|
||||||
"command": "cargo make --profile development-windows-x86 appflowy-core-dev"
|
"command": "cargo make --profile development-windows-x86 appflowy-core-dev"
|
||||||
},
|
},
|
||||||
"linux": {
|
"linux": {
|
||||||
"command": "cargo make --profile \"development-linux-$(uname -m)\" appflowy-core-dev"
|
"command": "cargo make --profile \"development-linux-$(uname -m)\" appflowy-core-dev"
|
||||||
},
|
},
|
||||||
"osx": {
|
"osx": {
|
||||||
"command": "cargo make --profile \"development-mac-$(uname -m)\" appflowy-core-dev"
|
"command": "cargo make --profile \"development-mac-$(uname -m)\" appflowy-core-dev"
|
||||||
},
|
},
|
||||||
"group": "build",
|
"group": "build",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "AF: Code Gen",
|
"label": "AF: Code Gen",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"dependsOrder": "sequence",
|
"dependsOrder": "sequence",
|
||||||
"dependsOn": [
|
"dependsOn": [
|
||||||
"AF: Flutter Clean",
|
"AF: Flutter Clean",
|
||||||
"AF: Flutter Pub Get",
|
"AF: Flutter Pub Get",
|
||||||
"AF: Flutter Package Get",
|
"AF: Flutter Package Get",
|
||||||
"AF: Generate Language Files",
|
"AF: Generate Language Files",
|
||||||
"AF: Generate Freezed Files"
|
"AF: Generate Freezed Files"
|
||||||
],
|
],
|
||||||
"group": {
|
"group": {
|
||||||
"kind": "build",
|
"kind": "build",
|
||||||
"isDefault": true
|
"isDefault": true
|
||||||
},
|
},
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"reveal": "always",
|
"reveal": "always",
|
||||||
"panel": "new"
|
"panel": "new"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "AF: Flutter Clean",
|
"label": "AF: Flutter Clean",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "flutter clean",
|
"command": "flutter clean",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}/appflowy_flutter"
|
"cwd": "${workspaceFolder}/appflowy_flutter"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "AF: Flutter Pub Get",
|
"label": "AF: Flutter Pub Get",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "flutter pub get",
|
"command": "flutter pub get",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}/appflowy_flutter"
|
"cwd": "${workspaceFolder}/appflowy_flutter"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "AF: Flutter Package Get",
|
"label": "AF: Flutter Package Get",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "flutter packages pub get",
|
"command": "flutter packages pub get",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}/appflowy_flutter"
|
"cwd": "${workspaceFolder}/appflowy_flutter"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "AF: Generate Freezed Files",
|
"label": "AF: Generate Freezed Files",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "flutter pub run build_runner build --delete-conflicting-outputs",
|
"command": "flutter pub run build_runner build --delete-conflicting-outputs",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}/appflowy_flutter"
|
"cwd": "${workspaceFolder}/appflowy_flutter"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "AF: Generate Language Files",
|
"label": "AF: Generate Language Files",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "sh ./scripts/generate_language_files.sh",
|
"command": "sh ./scripts/generate_language_files.sh",
|
||||||
"windows": {
|
"windows": {
|
||||||
"options": {
|
"options": {
|
||||||
"shell": {
|
"shell": {
|
||||||
"executable": "cmd.exe",
|
"executable": "cmd.exe",
|
||||||
"args": [
|
"args": [
|
||||||
"/d",
|
"/d",
|
||||||
"/c",
|
"/c",
|
||||||
".\\scripts\\generate_language_files.cmd"
|
".\\scripts\\generate_language_files.cmd"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"group": "build",
|
"group": "build",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "AF: Flutter Clean",
|
"label": "AF: Flutter Clean",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "cargo make flutter_clean",
|
"command": "cargo make flutter_clean",
|
||||||
"group": "build",
|
"group": "build",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "AF: flutter build aar",
|
"label": "AF: flutter build aar",
|
||||||
"type": "flutter",
|
"type": "flutter",
|
||||||
"command": "flutter",
|
"command": "flutter",
|
||||||
"args": [
|
"args": [
|
||||||
"build",
|
"build",
|
||||||
"aar"
|
"aar"
|
||||||
],
|
],
|
||||||
"group": "build",
|
"group": "build",
|
||||||
"problemMatcher": [],
|
"problemMatcher": [],
|
||||||
"detail": "appflowy_flutter"
|
"detail": "appflowy_flutter"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "AF: Tauri UI Dev",
|
"label": "AF: Tauri UI Dev",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"isBackground": true,
|
"isBackground": true,
|
||||||
"command": "yarn",
|
"command": "yarn",
|
||||||
"args": ["dev"],
|
"args": [
|
||||||
"options": {
|
"dev"
|
||||||
"cwd": "${workspaceFolder}/appflowy_tauri"
|
],
|
||||||
}
|
"options": {
|
||||||
},
|
"cwd": "${workspaceFolder}/appflowy_tauri"
|
||||||
{
|
}
|
||||||
"label": "AF: Tauri UI Build",
|
},
|
||||||
"type": "shell",
|
{
|
||||||
"command": "npm run build",
|
"label": "AF: Tauri UI Build",
|
||||||
"options": {
|
"type": "shell",
|
||||||
"cwd": "${workspaceFolder}/appflowy_tauri"
|
"command": "npm run build",
|
||||||
}
|
"options": {
|
||||||
},
|
"cwd": "${workspaceFolder}/appflowy_tauri"
|
||||||
{
|
}
|
||||||
"label": "AF: Tauri Dev",
|
},
|
||||||
"type": "shell",
|
{
|
||||||
"command": "npm run tauri:dev",
|
"label": "AF: Tauri Dev",
|
||||||
"options": {
|
"type": "shell",
|
||||||
"cwd": "${workspaceFolder}/appflowy_tauri"
|
"command": "npm run tauri:dev",
|
||||||
}
|
"options": {
|
||||||
},
|
"cwd": "${workspaceFolder}/appflowy_tauri"
|
||||||
{
|
}
|
||||||
"label": "AF: Tauri Clean",
|
},
|
||||||
"type": "shell",
|
{
|
||||||
"command": "cargo make tauri_clean",
|
"label": "AF: Tauri Clean",
|
||||||
"options": {
|
"type": "shell",
|
||||||
"cwd": "${workspaceFolder}"
|
"command": "cargo make tauri_clean",
|
||||||
}
|
"options": {
|
||||||
},
|
"cwd": "${workspaceFolder}"
|
||||||
{
|
}
|
||||||
"label": "AF: Tauri Clean + Dev",
|
},
|
||||||
"type": "shell",
|
{
|
||||||
"dependsOrder": "sequence",
|
"label": "AF: Tauri Clean + Dev",
|
||||||
"dependsOn": [
|
"type": "shell",
|
||||||
"AF: Tauri Clean",
|
"dependsOrder": "sequence",
|
||||||
"AF: Tauri UI Dev"
|
"dependsOn": [
|
||||||
],
|
"AF: Tauri Clean",
|
||||||
"options": {
|
"AF: Tauri UI Dev"
|
||||||
"cwd": "${workspaceFolder}"
|
],
|
||||||
}
|
"options": {
|
||||||
},
|
"cwd": "${workspaceFolder}"
|
||||||
{
|
}
|
||||||
"label": "AF: Tauri ESLint",
|
},
|
||||||
"type": "shell",
|
{
|
||||||
"command": "npx eslint --fix src",
|
"label": "AF: Tauri ESLint",
|
||||||
"options": {
|
"type": "shell",
|
||||||
"cwd": "${workspaceFolder}/appflowy_tauri"
|
"command": "npx eslint --fix src",
|
||||||
}
|
"options": {
|
||||||
},
|
"cwd": "${workspaceFolder}/appflowy_tauri"
|
||||||
]
|
}
|
||||||
}
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
4
frontend/appflowy_flutter/.gitignore
vendored
4
frontend/appflowy_flutter/.gitignore
vendored
@ -40,7 +40,7 @@ lib/generated_plugin_registrant.dart
|
|||||||
lib/generated/
|
lib/generated/
|
||||||
|
|
||||||
# Freezed generated files
|
# Freezed generated files
|
||||||
*.g.dart
|
*.g.dart
|
||||||
*.freezed.dart
|
*.freezed.dart
|
||||||
|
|
||||||
# Symbolication related
|
# Symbolication related
|
||||||
@ -67,4 +67,4 @@ windows/flutter/dart_ffi/
|
|||||||
**/**/*.so
|
**/**/*.so
|
||||||
**/**/Brewfile.lock.json
|
**/**/Brewfile.lock.json
|
||||||
**/.sandbox
|
**/.sandbox
|
||||||
**/.vscode/
|
**/.vscode/
|
||||||
|
BIN
frontend/appflowy_flutter/assets/test/workspaces/board.zip
Normal file
BIN
frontend/appflowy_flutter/assets/test/workspaces/board.zip
Normal file
Binary file not shown.
Binary file not shown.
@ -326,7 +326,8 @@
|
|||||||
"checklist": {
|
"checklist": {
|
||||||
"panelTitle": "Add an item"
|
"panelTitle": "Add an item"
|
||||||
},
|
},
|
||||||
"menuName": "Grid"
|
"menuName": "Grid",
|
||||||
|
"referencedGridPrefix": "View of"
|
||||||
},
|
},
|
||||||
"document": {
|
"document": {
|
||||||
"menuName": "Document",
|
"menuName": "Document",
|
||||||
@ -390,7 +391,8 @@
|
|||||||
"column": {
|
"column": {
|
||||||
"create_new_card": "New"
|
"create_new_card": "New"
|
||||||
},
|
},
|
||||||
"menuName": "Board"
|
"menuName": "Board",
|
||||||
|
"referencedBoardPrefix": "View of"
|
||||||
},
|
},
|
||||||
"calendar": {
|
"calendar": {
|
||||||
"menuName": "Calendar",
|
"menuName": "Calendar",
|
||||||
|
43
frontend/appflowy_flutter/integration_test/board_test.dart
Normal file
43
frontend/appflowy_flutter/integration_test/board_test.dart
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import 'package:appflowy_board/appflowy_board.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.board);
|
||||||
|
|
||||||
|
group('board', () {
|
||||||
|
setUpAll(() async => await service.setUpAll());
|
||||||
|
setUp(() async => await service.setUp());
|
||||||
|
|
||||||
|
testWidgets('integration test unzips the proper workspace and loads it correctly.', (tester) async {
|
||||||
|
await tester.initializeAppFlowy();
|
||||||
|
expect(find.byType(AppFlowyBoard), findsOneWidget);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,120 @@
|
|||||||
|
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';
|
||||||
|
import 'package:integration_test/integration_test.dart';
|
||||||
|
import 'util/keyboard.dart';
|
||||||
|
import 'util/util.dart';
|
||||||
|
|
||||||
|
/// Integration tests for an empty document. The [TestWorkspaceService] will load a workspace from an empty document `assets/test/workspaces/empty_document.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.emptyDocument);
|
||||||
|
|
||||||
|
group('Tests on a workspace with only an empty document', () {
|
||||||
|
setUpAll(() async => await service.setUpAll());
|
||||||
|
setUp(() async => await service.setUp());
|
||||||
|
|
||||||
|
testWidgets('/board shortcut creates a new board and view of the board', (tester) async {
|
||||||
|
await tester.initializeAppFlowy();
|
||||||
|
|
||||||
|
// Needs tab to obtain focus for the app flowy editor.
|
||||||
|
// by default the tap appears at the center of the widget.
|
||||||
|
final Finder editor = find.byType(AppFlowyEditor);
|
||||||
|
await tester.tap(editor);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
// tester.sendText() cannot be used since the editor
|
||||||
|
// does not contain any EditableText widgets.
|
||||||
|
// to interact with the app during an integration test,
|
||||||
|
// simulate physical keyboard events.
|
||||||
|
await FlowyTestKeyboard.simulateKeyDownEvent([
|
||||||
|
LogicalKeyboardKey.slash,
|
||||||
|
LogicalKeyboardKey.keyB,
|
||||||
|
LogicalKeyboardKey.keyO,
|
||||||
|
LogicalKeyboardKey.keyA,
|
||||||
|
LogicalKeyboardKey.keyR,
|
||||||
|
LogicalKeyboardKey.keyD,
|
||||||
|
LogicalKeyboardKey.arrowDown,
|
||||||
|
], tester: tester);
|
||||||
|
|
||||||
|
// Checks whether the options in the selection menu
|
||||||
|
// for /board exist.
|
||||||
|
expect(find.byType(SelectionMenuItemWidget), findsAtLeastNWidgets(2));
|
||||||
|
|
||||||
|
// Finalizes the slash command that creates the board.
|
||||||
|
await FlowyTestKeyboard.simulateKeyDownEvent([
|
||||||
|
LogicalKeyboardKey.enter,
|
||||||
|
], tester: tester);
|
||||||
|
|
||||||
|
// Checks whether new board is referenced and properly on the page.
|
||||||
|
expect(find.byType(BuiltInPageWidget), findsOneWidget);
|
||||||
|
|
||||||
|
// Checks whether the new database was created
|
||||||
|
const newBoardLabel = "Untitled";
|
||||||
|
expect(find.text(newBoardLabel), findsOneWidget);
|
||||||
|
|
||||||
|
// Checks whether a view of the database was created
|
||||||
|
const viewOfBoardLabel = "View of Untitled";
|
||||||
|
expect(find.text(viewOfBoardLabel), findsNWidgets(2));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('/grid shortcut creates a new grid and view of the grid', (tester) async {
|
||||||
|
await tester.initializeAppFlowy();
|
||||||
|
|
||||||
|
// Needs tab to obtain focus for the app flowy editor.
|
||||||
|
// by default the tap appears at the center of the widget.
|
||||||
|
final Finder editor = find.byType(AppFlowyEditor);
|
||||||
|
await tester.tap(editor);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
// tester.sendText() cannot be used since the editor
|
||||||
|
// does not contain any EditableText widgets.
|
||||||
|
// to interact with the app during an integration test,
|
||||||
|
// simulate physical keyboard events.
|
||||||
|
await FlowyTestKeyboard.simulateKeyDownEvent([
|
||||||
|
LogicalKeyboardKey.slash,
|
||||||
|
LogicalKeyboardKey.keyG,
|
||||||
|
LogicalKeyboardKey.keyR,
|
||||||
|
LogicalKeyboardKey.keyI,
|
||||||
|
LogicalKeyboardKey.keyD,
|
||||||
|
LogicalKeyboardKey.arrowDown,
|
||||||
|
], tester: tester);
|
||||||
|
|
||||||
|
// Checks whether the options in the selection menu
|
||||||
|
// for /grid exist.
|
||||||
|
expect(find.byType(SelectionMenuItemWidget), findsAtLeastNWidgets(2));
|
||||||
|
|
||||||
|
// Finalizes the slash command that creates the board.
|
||||||
|
await simulateKeyDownEvent(LogicalKeyboardKey.enter);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
// Checks whether new board is referenced and properly on the page.
|
||||||
|
expect(find.byType(BuiltInPageWidget), findsOneWidget);
|
||||||
|
|
||||||
|
// Checks whether the new database was created
|
||||||
|
const newTableLabel = "Untitled";
|
||||||
|
expect(find.text(newTableLabel), findsOneWidget);
|
||||||
|
|
||||||
|
// Checks whether a view of the database was created
|
||||||
|
const viewOfTableLabel = "View of Untitled";
|
||||||
|
expect(find.text(viewOfTableLabel), findsNWidgets(2));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
19
frontend/appflowy_flutter/integration_test/runner.dart
Normal file
19
frontend/appflowy_flutter/integration_test/runner.dart
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import 'package:integration_test/integration_test.dart';
|
||||||
|
|
||||||
|
import 'board_test.dart' as board_test;
|
||||||
|
import 'switch_folder_test.dart' as switch_folder_test;
|
||||||
|
import 'empty_document_test.dart' as empty_document_test;
|
||||||
|
|
||||||
|
/// The main task runner for all integration tests in AppFlowy.
|
||||||
|
///
|
||||||
|
/// Having a single entrypoint for integration tests is necessary due to an
|
||||||
|
/// [issue caused by switching files with integration testing](https://github.com/flutter/flutter/issues/101031).
|
||||||
|
/// If flutter/flutter#101031 is resolved, this file can be removed completely.
|
||||||
|
/// Once removed, the integration_test.yaml must be updated to exclude this as
|
||||||
|
/// as the test target.
|
||||||
|
void main() {
|
||||||
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
switch_folder_test.main();
|
||||||
|
board_test.main();
|
||||||
|
empty_document_test.main();
|
||||||
|
}
|
@ -1,10 +1,5 @@
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
|
||||||
import 'package:appflowy/plugins/document/presentation/plugins/base/built_in_page_widget.dart';
|
|
||||||
import 'package:appflowy/user/presentation/folder/folder_widget.dart';
|
import 'package:appflowy/user/presentation/folder/folder_widget.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
|
||||||
import 'package:flowy_infra_ui/style_widget/text_field.dart';
|
import 'package:flowy_infra_ui/style_widget/text_field.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:integration_test/integration_test.dart';
|
import 'package:integration_test/integration_test.dart';
|
||||||
|
|
||||||
@ -162,135 +157,5 @@ void main() {
|
|||||||
await TestFolder.currentLocation(),
|
await TestFolder.currentLocation(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('/board shortcut creates a new board', (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();
|
|
||||||
|
|
||||||
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
|
||||||
|
|
||||||
// Necessary for being able to enterText when not in debug mode
|
|
||||||
binding.testTextInput.register();
|
|
||||||
|
|
||||||
// Needs tab to obtain focus for the app flowy editor.
|
|
||||||
// by default the tap appears at the center of the widget.
|
|
||||||
final Finder editor = find.byType(AppFlowyEditor);
|
|
||||||
await tester.tap(editor);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
// tester.sendText() cannot be used since the editor
|
|
||||||
// does not contain any EditableText widgets.
|
|
||||||
// to interact with the app during an integration test,
|
|
||||||
// simulate physical keyboard events.
|
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.enter);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.enter);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.arrowLeft);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.slash);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.keyB);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.keyO);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.keyA);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.keyR);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.keyD);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
// Checks whether the options in the selection menu
|
|
||||||
// for /board exist.
|
|
||||||
expect(find.byType(SelectionMenuItemWidget), findsAtLeastNWidgets(2));
|
|
||||||
|
|
||||||
// Finalizes the slash command that creates the board.
|
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.enter);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
// Checks whether new board is referenced and properly on the page.
|
|
||||||
expect(find.byType(BuiltInPageWidget), findsOneWidget);
|
|
||||||
|
|
||||||
// Checks whether the new board is in the side bar.
|
|
||||||
final sidebarLabel = LocaleKeys.newPageText.tr();
|
|
||||||
expect(find.text(sidebarLabel), findsOneWidget);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('/grid shortcut creates a new grid', (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();
|
|
||||||
|
|
||||||
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
|
||||||
|
|
||||||
// Necessary for being able to enterText when not in debug mode
|
|
||||||
binding.testTextInput.register();
|
|
||||||
|
|
||||||
// Needs tab to obtain focus for the app flowy editor.
|
|
||||||
// by default the tap appears at the center of the widget.
|
|
||||||
final Finder editor = find.byType(AppFlowyEditor);
|
|
||||||
await tester.tap(editor);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
// tester.sendText() cannot be used since the editor
|
|
||||||
// does not contain any EditableText widgets.
|
|
||||||
// to interact with the app during an integration test,
|
|
||||||
// simulate physical keyboard events.
|
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.enter);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.enter);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.arrowLeft);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.slash);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.keyG);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.keyR);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.keyI);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.keyD);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
// Checks whether the options in the selection menu
|
|
||||||
// for /grid exist.
|
|
||||||
expect(find.byType(SelectionMenuItemWidget), findsAtLeastNWidgets(2));
|
|
||||||
|
|
||||||
// Finalizes the slash command that creates the board.
|
|
||||||
await simulateKeyDownEvent(LogicalKeyboardKey.enter);
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
// Checks whether new board is referenced and properly on the page.
|
|
||||||
expect(find.byType(BuiltInPageWidget), findsOneWidget);
|
|
||||||
|
|
||||||
// Checks whether the new board is in the side bar.
|
|
||||||
final sidebarLabel = LocaleKeys.newPageText.tr();
|
|
||||||
expect(find.text(sidebarLabel), findsOneWidget);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
66
frontend/appflowy_flutter/integration_test/util/data.dart
Normal file
66
frontend/appflowy_flutter/integration_test/util/data.dart
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
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;
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
enum TestWorkspace {
|
||||||
|
board("board"),
|
||||||
|
emptyDocument("empty_document");
|
||||||
|
|
||||||
|
const TestWorkspace(this._name);
|
||||||
|
|
||||||
|
final String _name;
|
||||||
|
|
||||||
|
Future<File> get zip async {
|
||||||
|
final Directory parent = await TestWorkspace._parent;
|
||||||
|
final File out = File(p.join(parent.path, '$_name.zip'));
|
||||||
|
if (await out.exists()) return out;
|
||||||
|
await out.create();
|
||||||
|
final ByteData data = await rootBundle.load(_asset);
|
||||||
|
await out.writeAsBytes(data.buffer.asUint8List());
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Directory> get root async {
|
||||||
|
final Directory parent = await TestWorkspace._parent;
|
||||||
|
return Directory(p.join(parent.path, _name));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Directory> get _parent async {
|
||||||
|
final Directory root = await getTemporaryDirectory();
|
||||||
|
if (await root.exists()) return root;
|
||||||
|
await root.create();
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
String get _asset => 'assets/test/workspaces/$_name.zip';
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestWorkspaceService {
|
||||||
|
const TestWorkspaceService(this.workspace);
|
||||||
|
|
||||||
|
final TestWorkspace workspace;
|
||||||
|
|
||||||
|
/// Instructs the application to read workspace data from the workspace found under this [TestWorkspace]'s path.
|
||||||
|
Future<void> setUpAll() async {
|
||||||
|
SharedPreferences.setMockInitialValues(
|
||||||
|
{
|
||||||
|
kSettingsLocationDefaultLocation:
|
||||||
|
await workspace.root.then((value) => value.path),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Workspaces that are checked into source are compressed. [TestWorkspaceService.setUp()] decompresses the file into an ephemeral directory that will be ignored by source control.
|
||||||
|
Future<void> setUp() async {
|
||||||
|
final inputStream =
|
||||||
|
InputFileStream(await workspace.zip.then((value) => value.path));
|
||||||
|
final archive = ZipDecoder().decodeBuffer(inputStream);
|
||||||
|
extractArchiveToDisk(
|
||||||
|
archive, await TestWorkspace._parent.then((value) => value.path));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart' as flutter_test;
|
||||||
|
|
||||||
|
class FlowyTestKeyboard {
|
||||||
|
static Future<void> simulateKeyDownEvent(List<LogicalKeyboardKey> keys,
|
||||||
|
{required flutter_test.WidgetTester tester}) async {
|
||||||
|
for (final LogicalKeyboardKey key in keys) {
|
||||||
|
await flutter_test.simulateKeyDownEvent(key);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
export 'base.dart';
|
export 'base.dart';
|
||||||
export 'launch.dart';
|
export 'launch.dart';
|
||||||
export 'settings.dart';
|
export 'settings.dart';
|
||||||
|
export 'data.dart';
|
||||||
|
@ -5,7 +5,7 @@ import 'package:dartz/dartz.dart';
|
|||||||
|
|
||||||
class DatabaseBackendService {
|
class DatabaseBackendService {
|
||||||
static Future<Either<List<DatabaseDescriptionPB>, FlowyError>>
|
static Future<Either<List<DatabaseDescriptionPB>, FlowyError>>
|
||||||
getAllDatabase() {
|
getAllDatabases() {
|
||||||
return DatabaseEventGetDatabases().send().then((result) {
|
return DatabaseEventGetDatabases().send().then((result) {
|
||||||
return result.fold((l) => left(l.items), (r) => right(r));
|
return result.fold((l) => left(l.items), (r) => right(r));
|
||||||
});
|
});
|
||||||
|
@ -1,20 +1,55 @@
|
|||||||
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/plugins/database_view/application/database_view_service.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/plugins/board/board_node_widget.dart';
|
import 'package:appflowy/plugins/document/presentation/plugins/board/board_node_widget.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/plugins/grid/grid_node_widget.dart';
|
import 'package:appflowy/plugins/document/presentation/plugins/grid/grid_node_widget.dart';
|
||||||
|
import 'package:appflowy/workspace/application/app/app_service.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/app.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/app.pb.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
|
||||||
const String kAppID = 'app_id';
|
const String kAppID = 'app_id';
|
||||||
const String kViewID = 'view_id';
|
const String kViewID = 'view_id';
|
||||||
|
|
||||||
extension InsertPage on EditorState {
|
extension InsertPage on EditorState {
|
||||||
void insertPage(AppPB appPB, ViewPB viewPB) {
|
Future<void> insertPage(AppPB appPB, ViewPB viewPB) async {
|
||||||
final selection = service.selectionService.currentSelection.value;
|
final selection = service.selectionService.currentSelection.value;
|
||||||
final textNodes =
|
final textNodes =
|
||||||
service.selectionService.currentSelectedNodes.whereType<TextNode>();
|
service.selectionService.currentSelectedNodes.whereType<TextNode>();
|
||||||
if (selection == null || textNodes.isEmpty) {
|
if (selection == null || textNodes.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the database that the view is associated with
|
||||||
|
final database =
|
||||||
|
await DatabaseViewBackendService(viewId: viewPB.id).openGrid().then(
|
||||||
|
(value) => value.getLeftOrNull(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (database == null) {
|
||||||
|
throw StateError(
|
||||||
|
'The database associated with ${viewPB.id} could not be found while attempting to create a referenced ${viewPB.layout.name}.');
|
||||||
|
}
|
||||||
|
|
||||||
|
final prefix = referencedBoardPrefix(viewPB.layout);
|
||||||
|
|
||||||
|
final ref = await AppBackendService().createView(
|
||||||
|
appId: appPB.id,
|
||||||
|
name: "$prefix ${viewPB.name}",
|
||||||
|
desc: appPB.desc,
|
||||||
|
layoutType: viewPB.layout,
|
||||||
|
ext: {
|
||||||
|
'database_id': database.id,
|
||||||
|
},
|
||||||
|
).then(
|
||||||
|
(value) => value.getLeftOrNull(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO(a-wallen): Show error dialog here.
|
||||||
|
if (ref == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final transaction = this.transaction;
|
final transaction = this.transaction;
|
||||||
transaction.insertNode(
|
transaction.insertNode(
|
||||||
selection.end.path,
|
selection.end.path,
|
||||||
@ -22,13 +57,24 @@ extension InsertPage on EditorState {
|
|||||||
type: _convertPageType(viewPB),
|
type: _convertPageType(viewPB),
|
||||||
attributes: {
|
attributes: {
|
||||||
kAppID: appPB.id,
|
kAppID: appPB.id,
|
||||||
kViewID: viewPB.id,
|
kViewID: ref.id,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
apply(transaction);
|
apply(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String referencedBoardPrefix(ViewLayoutTypePB layout) {
|
||||||
|
switch (layout) {
|
||||||
|
case ViewLayoutTypePB.Grid:
|
||||||
|
return LocaleKeys.grid_referencedGridPrefix.tr();
|
||||||
|
case ViewLayoutTypePB.Board:
|
||||||
|
return LocaleKeys.board_referencedBoardPrefix.tr();
|
||||||
|
default:
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String _convertPageType(ViewPB viewPB) {
|
String _convertPageType(ViewPB viewPB) {
|
||||||
switch (viewPB.layout) {
|
switch (viewPB.layout) {
|
||||||
case ViewLayoutTypePB.Grid:
|
case ViewLayoutTypePB.Grid:
|
||||||
|
@ -95,6 +95,7 @@ dependencies:
|
|||||||
http: ^0.13.5
|
http: ^0.13.5
|
||||||
json_annotation: ^4.7.0
|
json_annotation: ^4.7.0
|
||||||
path: ^1.8.2
|
path: ^1.8.2
|
||||||
|
archive: ^3.3.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_lints: ^2.0.1
|
flutter_lints: ^2.0.1
|
||||||
@ -163,30 +164,8 @@ flutter:
|
|||||||
- assets/images/common/
|
- assets/images/common/
|
||||||
- assets/images/grid/setting/
|
- assets/images/grid/setting/
|
||||||
- assets/translations/
|
- assets/translations/
|
||||||
# - images/a_dot_ham.jpeg
|
|
||||||
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
# The following assets will be excluded in release.
|
||||||
# https://flutter.dev/assets-and-images/#resolution-aware.
|
# BEGIN: EXCLUDE_IN_RELEASE
|
||||||
|
- assets/test/workspaces/
|
||||||
# For details regarding adding assets from package dependencies, see
|
# END: EXCLUDE_IN_RELEASE
|
||||||
# https://flutter.dev/assets-and-images/#from-packages
|
|
||||||
|
|
||||||
# To add custom fonts to your application, add a fonts section here,
|
|
||||||
# in this "flutter" section. Each entry in this list should have a
|
|
||||||
# "family" key with the font family name, and a "fonts" key with a
|
|
||||||
# list giving the asset and other descriptors for the font. For
|
|
||||||
# example:
|
|
||||||
# fonts:
|
|
||||||
# - family: Schyler
|
|
||||||
# fonts:
|
|
||||||
# - asset: fonts/Schyler-Regular.ttf
|
|
||||||
# - asset: fonts/Schyler-Italic.ttf
|
|
||||||
# style: italic
|
|
||||||
# - family: Trajan Pro
|
|
||||||
# fonts:
|
|
||||||
# - asset: fonts/TrajanPro.ttf
|
|
||||||
# - asset: fonts/TrajanPro_Bold.ttf
|
|
||||||
# weight: 700
|
|
||||||
#
|
|
||||||
# For details regarding fonts from package dependencies,
|
|
||||||
# see https://flutter.dev/custom-fonts/#from-packages
|
|
||||||
|
28
frontend/scripts/flutter_release_build/build_flowy.dart
Normal file
28
frontend/scripts/flutter_release_build/build_flowy.dart
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
part 'tool.dart';
|
||||||
|
|
||||||
|
const excludeTagBegin = 'BEGIN: EXCLUDE_IN_RELEASE';
|
||||||
|
const excludeTagEnd = 'END: EXCLUDE_IN_RELEASE';
|
||||||
|
|
||||||
|
Future<void> main(List<String> args) async {
|
||||||
|
const help = '''
|
||||||
|
A build script that modifies build assets before building the release version of AppFlowy.
|
||||||
|
|
||||||
|
args[0]: The directory that contains the AppFlowy git repository. Should be the parent to appflowy_flutter. (absolute path)
|
||||||
|
args[1]: The appflowy version to be built (github ref_name).
|
||||||
|
''';
|
||||||
|
const numArgs = 2;
|
||||||
|
assert(args.length == numArgs,
|
||||||
|
'Expected ${numArgs}, got ${args.length}. Read the following for instructions about how to use this script.\n\n$help');
|
||||||
|
if (args[0] == '-h' || args[0] == '--help') {
|
||||||
|
stdout.write(help);
|
||||||
|
stdout.flush();
|
||||||
|
}
|
||||||
|
final repositoryRoot = Directory(args[0]);
|
||||||
|
assert(await repositoryRoot.exists(),
|
||||||
|
'$repositoryRoot is an invalid directory. Please try again with a valid directory.\n\n$help');
|
||||||
|
final appVersion = args[1];
|
||||||
|
await _BuildTool(repositoryRoot: repositoryRoot.path, appVersion: appVersion)
|
||||||
|
.run();
|
||||||
|
}
|
115
frontend/scripts/flutter_release_build/tool.dart
Normal file
115
frontend/scripts/flutter_release_build/tool.dart
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
part of 'build_flowy.dart';
|
||||||
|
|
||||||
|
enum _ScanMode {
|
||||||
|
ignore,
|
||||||
|
target,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum _ModifyMode {
|
||||||
|
include,
|
||||||
|
exclude,
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BuildTool {
|
||||||
|
const _BuildTool({
|
||||||
|
required this.repositoryRoot,
|
||||||
|
required this.appVersion,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String repositoryRoot;
|
||||||
|
final String appVersion;
|
||||||
|
|
||||||
|
String get projectRoot =>
|
||||||
|
[repositoryRoot, 'appflowy_flutter'].join(Platform.pathSeparator);
|
||||||
|
|
||||||
|
File get pubspec =>
|
||||||
|
File([projectRoot, 'pubspec.yaml'].join(Platform.pathSeparator));
|
||||||
|
|
||||||
|
Future<String> get _architecture async =>
|
||||||
|
await Process.run('uname', ['-m']).then((value) => value.stdout.trim());
|
||||||
|
|
||||||
|
Future<String> get _commandForOS async {
|
||||||
|
// Check the operating system and CPU architecture
|
||||||
|
var os = Platform.operatingSystem;
|
||||||
|
var arch = Platform.isMacOS ? await _architecture : Platform.localHostname;
|
||||||
|
|
||||||
|
// Determine the appropriate command based on the OS and architecture
|
||||||
|
if (os == 'windows') {
|
||||||
|
return 'cargo make --env APP_VERSION=$appVersion --profile production-windows-x86 appflowy';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (os == 'linux') {
|
||||||
|
return 'cargo make --env APP_VERSION=$appVersion --profile production-linux-x86_64 appflowy';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (os == 'macos') {
|
||||||
|
if (arch == 'x86_64') {
|
||||||
|
return 'cargo make --env APP_VERSION=$appVersion --profile production-mac-x86_64 appflowy';
|
||||||
|
}
|
||||||
|
if (arch == 'arm64') {
|
||||||
|
return 'cargo make --env APP_VERSION=$appVersion --profile production-mac-arm64 appflowy';
|
||||||
|
}
|
||||||
|
throw 'Unsupported CPU architecture: $arch';
|
||||||
|
}
|
||||||
|
|
||||||
|
throw 'Unsupported operating system: $os';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scans a file for lines between # BEGIN: EXCLUDE_IN_RELEASE and
|
||||||
|
/// END: EXCLUDE_IN_RELEASE. Will add a comment to remove those assets
|
||||||
|
/// from the build.
|
||||||
|
Future<void> _process_directives(
|
||||||
|
File file, {
|
||||||
|
required _ModifyMode mode,
|
||||||
|
}) async {
|
||||||
|
// Read the contents of the file into a list
|
||||||
|
var lines = await file.readAsLines();
|
||||||
|
|
||||||
|
// Find the lines between BEGIN: EXCLUDE_IN_RELEASE and END: EXCLUDE_IN_RELEASE
|
||||||
|
var scanMode = _ScanMode.ignore;
|
||||||
|
for (var i = 0; i < lines.length; i++) {
|
||||||
|
var line = lines[i];
|
||||||
|
if (line.contains(excludeTagBegin)) {
|
||||||
|
scanMode = _ScanMode.target;
|
||||||
|
} else if (line.contains(excludeTagEnd)) {
|
||||||
|
scanMode = _ScanMode.ignore;
|
||||||
|
} else if (scanMode == _ScanMode.target) {
|
||||||
|
lines[i] = _modify(line, mode: mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the modified contents back to the file
|
||||||
|
await file.writeAsString(lines.join('\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
String _modify(String line, {required _ModifyMode mode}) {
|
||||||
|
switch (mode) {
|
||||||
|
case _ModifyMode.include:
|
||||||
|
return line.split('#').where((element) => element != '#').join();
|
||||||
|
case _ModifyMode.exclude:
|
||||||
|
return '#$line';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _build() async {
|
||||||
|
final cwd = Directory.current;
|
||||||
|
Directory.current = repositoryRoot;
|
||||||
|
|
||||||
|
final cmd = await _commandForOS;
|
||||||
|
// Run the command using the Process.run() function
|
||||||
|
// final build = await Process.run('echo', ['hello'], runInShell: true);
|
||||||
|
final build =
|
||||||
|
await Process.start(cmd.split(' ')[0], cmd.split(' ').sublist(1));
|
||||||
|
await stdout.addStream(build.stdout);
|
||||||
|
await stderr.addStream(build.stderr);
|
||||||
|
Directory.current = cwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> run() async {
|
||||||
|
final pubspec = this.pubspec;
|
||||||
|
|
||||||
|
await _process_directives(pubspec, mode: _ModifyMode.exclude);
|
||||||
|
await _build();
|
||||||
|
await _process_directives(pubspec, mode: _ModifyMode.include);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user