Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5d9a73194 | ||
|
|
619d82f115 | ||
|
|
d5887b0a12 | ||
|
|
c7f73551c7 | ||
|
|
ed04e247ba | ||
|
|
2ecd0a67ad | ||
|
|
4ce94a9ceb | ||
|
|
d4e39389d2 | ||
|
|
ab5a3dae3c | ||
|
|
133ac72317 | ||
|
|
e9be37a961 | ||
|
|
0f9f5251f2 |
No files matched your search
@@ -23,7 +23,7 @@ on:
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.10.1"
|
||||
RUST_TOOLCHAIN: "1.65"
|
||||
RUST_TOOLCHAIN: "1.70"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
with:
|
||||
prefix-key: ${{ matrix.os }}
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
frontend/rust-lib
|
||||
|
||||
- uses: davidB/rust-cargo-make@v1
|
||||
with:
|
||||
@@ -118,3 +118,10 @@ 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
|
||||
@@ -6,14 +6,18 @@ on:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/appflowy_flutter/**"
|
||||
- ".github/workflows/flutter_ci.yaml"
|
||||
- "frontend/**"
|
||||
- "!frontend/appflowy_tauri/**"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/appflowy_flutter/**"
|
||||
- ".github/workflows/flutter_ci.yaml"
|
||||
- "frontend/**"
|
||||
- "!frontend/appflowy_tauri/**"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -116,10 +120,9 @@ jobs:
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
# - uses: codecov/codecov-action@v3
|
||||
# with:
|
||||
# name: appflowy
|
||||
# flags: appflowy
|
||||
# env_vars: ${{ matrix.os }}
|
||||
# fail_ci_if_error: true
|
||||
# verbose: true
|
||||
- uses: codecov/codecov-action@v3
|
||||
with:
|
||||
name: appflowy
|
||||
flags: appflowy_flutter_integrateion_test
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.10.1"
|
||||
RUST_TOOLCHAIN: "1.65"
|
||||
RUST_TOOLCHAIN: "1.70"
|
||||
|
||||
jobs:
|
||||
create-release:
|
||||
|
||||
@@ -21,7 +21,7 @@ on:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUST_TOOLCHAIN: "1.65"
|
||||
RUST_TOOLCHAIN: "1.70"
|
||||
FLUTTER_VERSION: "3.10.1"
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -12,7 +12,7 @@ on:
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
FLUTTER_VERSION: "3.10.1"
|
||||
RUST_TOOLCHAIN: "1.65"
|
||||
RUST_TOOLCHAIN: "1.70"
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
|
||||
@@ -8,7 +8,7 @@ on:
|
||||
env:
|
||||
NODE_VERSION: "18.16.0"
|
||||
PNPM_VERSION: "8.5.0"
|
||||
RUST_TOOLCHAIN: "1.65"
|
||||
RUST_TOOLCHAIN: "1.70"
|
||||
|
||||
jobs:
|
||||
tauri-build:
|
||||
|
||||
@@ -1,5 +1,55 @@
|
||||
# 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
|
||||
|
||||
@@ -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.0"
|
||||
CURRENT_APP_VERSION = "0.2.1"
|
||||
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite"
|
||||
PRODUCT_NAME = "AppFlowy"
|
||||
# CRATE_TYPE: https://doc.rust-lang.org/reference/linkage.html
|
||||
|
||||
@@ -70,3 +70,5 @@ windows/flutter/dart_ffi/
|
||||
**/.vscode/
|
||||
|
||||
*.env
|
||||
|
||||
coverage/
|
||||
@@ -0,0 +1,7 @@
|
||||
<?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>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,12 @@
|
||||
<?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>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -368,11 +368,16 @@
|
||||
"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",
|
||||
@@ -459,7 +464,9 @@
|
||||
"layoutDateField": "Layout calendar by",
|
||||
"noDateTitle": "No Date",
|
||||
"noDateHint": "Unscheduled events will show up here",
|
||||
"clickToAdd": "Click to add to the calendar"
|
||||
}
|
||||
"clickToAdd": "Click to add to the calendar",
|
||||
"name": "Calendar layout"
|
||||
},
|
||||
"referencedCalendarPrefix": "View of"
|
||||
}
|
||||
}
|
||||
@@ -35,9 +35,7 @@ void main() {
|
||||
setUpAll(() async => await service.setUpAll());
|
||||
setUp(() async => await service.setUp());
|
||||
|
||||
testWidgets(
|
||||
'integration test unzips the proper workspace and loads it correctly.',
|
||||
(tester) async {
|
||||
testWidgets('open the board with data structure in v0.2.0', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
expect(find.byType(AppFlowyBoard), findsOneWidget);
|
||||
});
|
||||
|
||||
@@ -1,54 +1,35 @@
|
||||
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', () {
|
||||
setUpAll(() async => await service.setUpAll());
|
||||
setUp(() async => await service.setUp());
|
||||
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);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'hovering on cover image will display change and delete cover image buttons',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
expect(find.byType(Image), findsOneWidget);
|
||||
|
||||
final TestPointer pointer = TestPointer(1, PointerDeviceKind.mouse);
|
||||
final imageFinder = find.byType(Image);
|
||||
final Offset offset = tester.getCenter(imageFinder);
|
||||
|
||||
pointer.hover(offset);
|
||||
expect(find.byType(RoundedTextButton), findsOneWidget);
|
||||
await tester.tapGoButton();
|
||||
await tester.hoverOnCoverPluginAddButton();
|
||||
await tester.expectToSeePluginAddCoverAndIconButton();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
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;
|
||||
import 'open_ai_smart_menu_test.dart' as smart_menu_test;
|
||||
import 'document_test.dart' as document_test;
|
||||
import 'cover_image_test.dart' as cover_image_test;
|
||||
|
||||
/// The main task runner for all integration tests in AppFlowy.
|
||||
///
|
||||
@@ -16,8 +14,9 @@ import 'document_test.dart' as document_test;
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
switch_folder_test.main();
|
||||
board_test.main();
|
||||
empty_document_test.main();
|
||||
smart_menu_test.main();
|
||||
cover_image_test.main();
|
||||
document_test.main();
|
||||
// board_test.main();
|
||||
// empty_document_test.main();
|
||||
// smart_menu_test.main();
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
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,5 +1,4 @@
|
||||
import 'package:appflowy/user/presentation/folder/folder_widget.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text_field.dart';
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
@@ -25,60 +24,12 @@ 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();
|
||||
|
||||
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);
|
||||
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();
|
||||
@@ -88,6 +39,7 @@ void main() {
|
||||
|
||||
// switch to user B
|
||||
{
|
||||
// set user name to userA
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
await tester.enterUserName(userA);
|
||||
@@ -100,14 +52,15 @@ void main() {
|
||||
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);
|
||||
}
|
||||
|
||||
// switch to the userA
|
||||
{
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
await tester.enterUserName(userB);
|
||||
|
||||
await tester.openSettingsPage(SettingsPage.files);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
@@ -117,7 +70,7 @@ void main() {
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
tester.expectToSeeWelcomePage();
|
||||
expect(find.textContaining(userA), findsOneWidget);
|
||||
tester.expectToSeeUserName(userA);
|
||||
}
|
||||
|
||||
// switch to the userB again
|
||||
@@ -132,7 +85,7 @@ void main() {
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
tester.expectToSeeWelcomePage();
|
||||
expect(find.textContaining(userB), findsOneWidget);
|
||||
tester.expectToSeeUserName(userB);
|
||||
}
|
||||
|
||||
await TestFolder.cleanTestLocation(userA);
|
||||
|
||||
+60
-2
@@ -2,18 +2,21 @@ 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 AppFlowyLaunch on WidgetTester {
|
||||
extension CommonOperations on WidgetTester {
|
||||
Future<void> tapGoButton() async {
|
||||
final goButton = find.byType(GoButton);
|
||||
await tapButton(goButton);
|
||||
@@ -25,7 +28,7 @@ extension AppFlowyLaunch on WidgetTester {
|
||||
|
||||
void expectToSeeWelcomePage() {
|
||||
expect(find.byType(HomeStack), findsOneWidget);
|
||||
expect(find.textContaining('Read me'), findsNWidgets(2));
|
||||
expect(find.textContaining('Read me'), findsOneWidget);
|
||||
}
|
||||
|
||||
Future<void> tapAddButton() async {
|
||||
@@ -97,4 +100,59 @@ extension AppFlowyLaunch on WidgetTester {
|
||||
);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -49,9 +49,11 @@ 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: await workspace.root.then((value) => value.path),
|
||||
KVKeys.pathLocation: path,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 extends FilePicker {
|
||||
class MockFilePicker implements FilePickerService {
|
||||
MockFilePicker({
|
||||
required this.mockPath,
|
||||
});
|
||||
@@ -15,6 +15,34 @@ class MockFilePicker extends FilePicker {
|
||||
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 {
|
||||
@@ -27,3 +55,15 @@ 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,33 +1,13 @@
|
||||
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 {
|
||||
@@ -41,7 +21,9 @@ extension AppFlowySettings on WidgetTester {
|
||||
|
||||
/// Open the page that insides the settings page
|
||||
Future<void> openSettingsPage(SettingsPage page) async {
|
||||
final button = find.text(page.name, findRichText: true);
|
||||
final button = find.byWidgetPredicate(
|
||||
(widget) => widget is SettingsMenuElement && widget.page == page,
|
||||
);
|
||||
expect(button, findsOneWidget);
|
||||
await tapButton(button);
|
||||
return;
|
||||
@@ -50,7 +32,7 @@ extension AppFlowySettings on WidgetTester {
|
||||
/// Restore the AppFlowy data storage location
|
||||
Future<void> restoreLocation() async {
|
||||
final button =
|
||||
find.byTooltip(LocaleKeys.settings_files_restoreLocation.tr());
|
||||
find.byTooltip(LocaleKeys.settings_files_recoverLocationTooltips.tr());
|
||||
expect(button, findsOneWidget);
|
||||
await tapButton(button);
|
||||
return;
|
||||
@@ -64,8 +46,9 @@ extension AppFlowySettings on WidgetTester {
|
||||
}
|
||||
|
||||
Future<void> tapCustomLocationButton() async {
|
||||
final button =
|
||||
find.byTooltip(LocaleKeys.settings_files_customizeLocation.tr());
|
||||
final button = find.byTooltip(
|
||||
LocaleKeys.settings_files_changeLocationTooltips.tr(),
|
||||
);
|
||||
expect(button, findsOneWidget);
|
||||
await tapButton(button);
|
||||
return;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export 'base.dart';
|
||||
export 'launch.dart';
|
||||
export 'common_operations.dart';
|
||||
export 'settings.dart';
|
||||
export 'data.dart';
|
||||
@@ -43,7 +43,7 @@ class BlankPagePluginWidgetBuilder extends PluginWidgetBuilder
|
||||
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
-1
@@ -47,7 +47,7 @@ class DateCellDataPersistence implements CellDataPersistence<DateCellData> {
|
||||
@override
|
||||
Future<Option<FlowyError>> save(DateCellData data) {
|
||||
final payload = DateChangesetPB.create()
|
||||
..cellPath = _makeCellPath(cellContext);
|
||||
..cellId = _makeCellPath(cellContext);
|
||||
if (data.dateTime != null) {
|
||||
final date = (data.dateTime!.millisecondsSinceEpoch ~/ 1000).toString();
|
||||
payload.date = date;
|
||||
|
||||
+22
-13
@@ -80,6 +80,7 @@ class DatabaseController {
|
||||
final DatabaseViewBackendService _databaseViewBackendSvc;
|
||||
final FieldController fieldController;
|
||||
DatabaseLayoutPB? databaseLayout;
|
||||
DatabaseLayoutSettingPB? databaseLayoutSetting;
|
||||
late DatabaseViewCache _viewCache;
|
||||
|
||||
// Callbacks
|
||||
@@ -92,17 +93,17 @@ class DatabaseController {
|
||||
RowCache get rowCache => _viewCache.rowCache;
|
||||
|
||||
// Listener
|
||||
final DatabaseGroupListener groupListener;
|
||||
final DatabaseLayoutSettingListener layoutListener;
|
||||
final DatabaseCalendarLayoutListener calendarLayoutListener;
|
||||
final DatabaseGroupListener _groupListener;
|
||||
final DatabaseLayoutSettingListener _layoutListener;
|
||||
final DatabaseCalendarLayoutListener _calendarLayoutListener;
|
||||
|
||||
DatabaseController({required ViewPB view})
|
||||
: 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 = DatabaseLayoutSettingListener(view.id),
|
||||
_calendarLayoutListener = DatabaseCalendarLayoutListener(view.id) {
|
||||
_viewCache = DatabaseViewCache(
|
||||
viewId: viewId,
|
||||
fieldController: fieldController,
|
||||
@@ -224,7 +225,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;
|
||||
@@ -248,7 +249,12 @@ class DatabaseController {
|
||||
if (databaseLayout != null) {
|
||||
_databaseViewBackendSvc.getLayoutSetting(databaseLayout!).then((result) {
|
||||
result.fold(
|
||||
(l) => _layoutCallbacks?.onLoadLayout(l),
|
||||
(newDatabaseLayoutSetting) {
|
||||
databaseLayoutSetting = newDatabaseLayoutSetting;
|
||||
databaseLayoutSetting?.freeze();
|
||||
|
||||
_layoutCallbacks?.onLoadLayout(newDatabaseLayoutSetting);
|
||||
},
|
||||
(r) => Log.error(r),
|
||||
);
|
||||
});
|
||||
@@ -285,7 +291,7 @@ class DatabaseController {
|
||||
}
|
||||
|
||||
void _listenOnGroupChanged() {
|
||||
groupListener.start(
|
||||
_groupListener.start(
|
||||
onNumOfGroupsChanged: (result) {
|
||||
result.fold(
|
||||
(changeset) {
|
||||
@@ -316,11 +322,14 @@ class DatabaseController {
|
||||
}
|
||||
|
||||
void _listenOnLayoutChanged() {
|
||||
layoutListener.start(
|
||||
_layoutListener.start(
|
||||
onLayoutChanged: (result) {
|
||||
result.fold(
|
||||
(l) {
|
||||
_layoutCallbacks?.onLayoutChanged(l);
|
||||
(newDatabaseLayoutSetting) {
|
||||
databaseLayoutSetting = newDatabaseLayoutSetting;
|
||||
databaseLayoutSetting?.freeze();
|
||||
|
||||
_layoutCallbacks?.onLayoutChanged(newDatabaseLayoutSetting);
|
||||
},
|
||||
(r) => Log.error(r),
|
||||
);
|
||||
@@ -329,7 +338,7 @@ class DatabaseController {
|
||||
}
|
||||
|
||||
void _listenOnCalendarLayoutChanged() {
|
||||
calendarLayoutListener.start(
|
||||
_calendarLayoutListener.start(
|
||||
onCalendarLayoutChanged: (result) {
|
||||
result.fold(
|
||||
(l) {
|
||||
|
||||
+1
-4
@@ -1,5 +1,4 @@
|
||||
import 'package:appflowy_backend/dispatch/dispatch.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-error/errors.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
@@ -10,12 +9,10 @@ class GroupBackendService {
|
||||
|
||||
Future<Either<Unit, FlowyError>> groupByField({
|
||||
required String fieldId,
|
||||
required FieldType fieldType,
|
||||
}) {
|
||||
final payload = GroupByFieldPayloadPB.create()
|
||||
..viewId = viewId
|
||||
..fieldId = fieldId
|
||||
..fieldType = fieldType;
|
||||
..fieldId = fieldId;
|
||||
|
||||
return DatabaseEventSetGroupByField(payload).send();
|
||||
}
|
||||
|
||||
-1
@@ -32,7 +32,6 @@ class DatabaseGroupBloc extends Bloc<DatabaseGroupEvent, DatabaseGroupState> {
|
||||
setGroupByField: (String fieldId, FieldType fieldType) async {
|
||||
final result = await _groupBackendSvc.groupByField(
|
||||
fieldId: fieldId,
|
||||
fieldType: fieldType,
|
||||
);
|
||||
result.fold((l) => null, (err) => Log.error(err));
|
||||
},
|
||||
|
||||
+31
@@ -1,4 +1,5 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
@@ -45,6 +46,7 @@ enum DatabaseSettingAction {
|
||||
showProperties,
|
||||
showLayout,
|
||||
showGroup,
|
||||
showCalendarLayout,
|
||||
}
|
||||
|
||||
extension DatabaseSettingActionExtension on DatabaseSettingAction {
|
||||
@@ -56,6 +58,8 @@ extension DatabaseSettingActionExtension on DatabaseSettingAction {
|
||||
return 'grid/setting/database_layout';
|
||||
case DatabaseSettingAction.showGroup:
|
||||
return 'grid/setting/group';
|
||||
case DatabaseSettingAction.showCalendarLayout:
|
||||
return 'grid/setting/calendar_layout';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +71,33 @@ extension DatabaseSettingActionExtension on DatabaseSettingAction {
|
||||
return LocaleKeys.grid_settings_databaseLayout.tr();
|
||||
case DatabaseSettingAction.showGroup:
|
||||
return LocaleKeys.grid_settings_group.tr();
|
||||
case DatabaseSettingAction.showCalendarLayout:
|
||||
return LocaleKeys.calendar_settings_name.tr();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the list of actions that should be shown for the given database layout.
|
||||
List<DatabaseSettingAction> actionsForDatabaseLayout(DatabaseLayoutPB? layout) {
|
||||
switch (layout) {
|
||||
case DatabaseLayoutPB.Board:
|
||||
return [
|
||||
DatabaseSettingAction.showProperties,
|
||||
DatabaseSettingAction.showLayout,
|
||||
DatabaseSettingAction.showGroup,
|
||||
];
|
||||
case DatabaseLayoutPB.Calendar:
|
||||
return [
|
||||
DatabaseSettingAction.showProperties,
|
||||
DatabaseSettingAction.showLayout,
|
||||
DatabaseSettingAction.showCalendarLayout,
|
||||
];
|
||||
case DatabaseLayoutPB.Grid:
|
||||
return [
|
||||
DatabaseSettingAction.showProperties,
|
||||
DatabaseSettingAction.showLayout,
|
||||
];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -230,7 +230,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
for (final group in updatedGroups) {
|
||||
final columnController =
|
||||
boardController.getGroupController(group.groupId);
|
||||
columnController?.updateGroupName(group.desc);
|
||||
columnController?.updateGroupName(group.groupName);
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -285,7 +285,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
AppFlowyGroupData initializeGroupData(GroupPB group) {
|
||||
return AppFlowyGroupData(
|
||||
id: group.groupId,
|
||||
name: group.desc,
|
||||
name: group.groupName,
|
||||
items: _buildGroupItems(group),
|
||||
customData: GroupData(
|
||||
group: group,
|
||||
|
||||
@@ -69,11 +69,11 @@ class BoardPluginWidgetBuilder extends PluginWidgetBuilder {
|
||||
Widget get leftBarItem => ViewLeftBarItem(view: view);
|
||||
|
||||
@override
|
||||
Widget buildWidget(PluginContext context) {
|
||||
Widget buildWidget({PluginContext? context}) {
|
||||
notifier.isDeleted.addListener(() {
|
||||
notifier.isDeleted.value.fold(() => null, (deletedView) {
|
||||
if (deletedView.hasIndex()) {
|
||||
context.onDeleted(view, deletedView.index);
|
||||
context?.onDeleted(view, deletedView.index);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
+51
-17
@@ -1,3 +1,5 @@
|
||||
import 'package:appflowy/plugins/database_view/application/layout/layout_setting_listener.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
@@ -9,10 +11,19 @@ typedef DayOfWeek = int;
|
||||
|
||||
class CalendarSettingBloc
|
||||
extends Bloc<CalendarSettingEvent, CalendarSettingState> {
|
||||
CalendarSettingBloc({required CalendarLayoutSettingPB? layoutSettings})
|
||||
: super(CalendarSettingState.initial(layoutSettings)) {
|
||||
final String viewId;
|
||||
final DatabaseLayoutSettingListener _listener;
|
||||
|
||||
CalendarSettingBloc({
|
||||
required this.viewId,
|
||||
required CalendarLayoutSettingPB? layoutSettings,
|
||||
}) : _listener = DatabaseLayoutSettingListener(viewId),
|
||||
super(CalendarSettingState.initial(layoutSettings)) {
|
||||
on<CalendarSettingEvent>((event, emit) {
|
||||
event.when(
|
||||
init: () {
|
||||
_startListening();
|
||||
},
|
||||
performAction: (action) {
|
||||
emit(state.copyWith(selectedAction: Some(action)));
|
||||
},
|
||||
@@ -22,6 +33,44 @@ class CalendarSettingBloc
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
_listener.start(
|
||||
onLayoutChanged: (result) {
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
|
||||
result.fold(
|
||||
(setting) =>
|
||||
add(CalendarSettingEvent.updateLayoutSetting(setting.calendar)),
|
||||
(r) => Log.error(r),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _listener.stop();
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class CalendarSettingEvent with _$CalendarSettingEvent {
|
||||
const factory CalendarSettingEvent.init() = _Init;
|
||||
const factory CalendarSettingEvent.performAction(
|
||||
CalendarSettingAction action,
|
||||
) = _PerformAction;
|
||||
const factory CalendarSettingEvent.updateLayoutSetting(
|
||||
CalendarLayoutSettingPB setting,
|
||||
) = _UpdateLayoutSetting;
|
||||
}
|
||||
|
||||
enum CalendarSettingAction {
|
||||
properties,
|
||||
layout,
|
||||
}
|
||||
|
||||
@freezed
|
||||
@@ -39,18 +88,3 @@ class CalendarSettingState with _$CalendarSettingState {
|
||||
layoutSetting: layoutSettings == null ? none() : Some(layoutSettings),
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class CalendarSettingEvent with _$CalendarSettingEvent {
|
||||
const factory CalendarSettingEvent.performAction(
|
||||
CalendarSettingAction action,
|
||||
) = _PerformAction;
|
||||
const factory CalendarSettingEvent.updateLayoutSetting(
|
||||
CalendarLayoutSettingPB setting,
|
||||
) = _UpdateLayoutSetting;
|
||||
}
|
||||
|
||||
enum CalendarSettingAction {
|
||||
properties,
|
||||
layout,
|
||||
}
|
||||
@@ -69,11 +69,11 @@ class CalendarPluginWidgetBuilder extends PluginWidgetBuilder {
|
||||
Widget get leftBarItem => ViewLeftBarItem(view: view);
|
||||
|
||||
@override
|
||||
Widget buildWidget(PluginContext context) {
|
||||
Widget buildWidget({PluginContext? context}) {
|
||||
notifier.isDeleted.addListener(() {
|
||||
notifier.isDeleted.value.fold(() => null, (deletedView) {
|
||||
if (deletedView.hasIndex()) {
|
||||
context.onDeleted(view, deletedView.index);
|
||||
context?.onDeleted(view, deletedView.index);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
+98
-85
@@ -15,17 +15,25 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:protobuf/protobuf.dart';
|
||||
|
||||
import 'calendar_setting.dart';
|
||||
abstract class ICalendarSetting {
|
||||
/// Returns the current layout settings for the calendar view.
|
||||
CalendarLayoutSettingPB? getLayoutSetting();
|
||||
|
||||
/// Updates the layout settings for the calendar view.
|
||||
void updateLayoutSettings(CalendarLayoutSettingPB layoutSettings);
|
||||
}
|
||||
|
||||
/// Widget that displays a list of settings that alters the appearance of the
|
||||
/// calendar
|
||||
class CalendarLayoutSetting extends StatefulWidget {
|
||||
final CalendarSettingContext settingContext;
|
||||
final Function(CalendarLayoutSettingPB? layoutSettings) onUpdated;
|
||||
final String viewId;
|
||||
final FieldController fieldController;
|
||||
final ICalendarSetting calendarSettingController;
|
||||
|
||||
const CalendarLayoutSetting({
|
||||
required this.onUpdated,
|
||||
required this.settingContext,
|
||||
required this.viewId,
|
||||
required this.fieldController,
|
||||
required this.calendarSettingController,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@@ -44,85 +52,90 @@ class _CalendarLayoutSettingState extends State<CalendarLayoutSetting> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<CalendarSettingBloc, CalendarSettingState>(
|
||||
builder: (context, state) {
|
||||
final CalendarLayoutSettingPB? settings = state.layoutSetting
|
||||
.foldLeft(null, (previous, settings) => settings);
|
||||
|
||||
if (settings == null) {
|
||||
return const CircularProgressIndicator();
|
||||
}
|
||||
final availableSettings = _availableCalendarSettings(settings);
|
||||
|
||||
final items = availableSettings.map((setting) {
|
||||
switch (setting) {
|
||||
case CalendarLayoutSettingAction.showWeekNumber:
|
||||
return ShowWeekNumber(
|
||||
showWeekNumbers: settings.showWeekNumbers,
|
||||
onUpdated: (showWeekNumbers) {
|
||||
_updateLayoutSettings(
|
||||
context,
|
||||
showWeekNumbers: showWeekNumbers,
|
||||
onUpdated: widget.onUpdated,
|
||||
);
|
||||
},
|
||||
);
|
||||
case CalendarLayoutSettingAction.showWeekends:
|
||||
return ShowWeekends(
|
||||
showWeekends: settings.showWeekends,
|
||||
onUpdated: (showWeekends) {
|
||||
_updateLayoutSettings(
|
||||
context,
|
||||
showWeekends: showWeekends,
|
||||
onUpdated: widget.onUpdated,
|
||||
);
|
||||
},
|
||||
);
|
||||
case CalendarLayoutSettingAction.firstDayOfWeek:
|
||||
return FirstDayOfWeek(
|
||||
firstDayOfWeek: settings.firstDayOfWeek,
|
||||
popoverMutex: popoverMutex,
|
||||
onUpdated: (firstDayOfWeek) {
|
||||
_updateLayoutSettings(
|
||||
context,
|
||||
onUpdated: widget.onUpdated,
|
||||
firstDayOfWeek: firstDayOfWeek,
|
||||
);
|
||||
},
|
||||
);
|
||||
case CalendarLayoutSettingAction.layoutField:
|
||||
return LayoutDateField(
|
||||
fieldController: widget.settingContext.fieldController,
|
||||
viewId: widget.settingContext.viewId,
|
||||
fieldId: settings.fieldId,
|
||||
popoverMutex: popoverMutex,
|
||||
onUpdated: (fieldId) {
|
||||
_updateLayoutSettings(
|
||||
context,
|
||||
onUpdated: widget.onUpdated,
|
||||
layoutFieldId: fieldId,
|
||||
);
|
||||
},
|
||||
);
|
||||
default:
|
||||
return const SizedBox();
|
||||
}
|
||||
}).toList();
|
||||
|
||||
return SizedBox(
|
||||
width: 200,
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
controller: ScrollController(),
|
||||
itemCount: items.length,
|
||||
separatorBuilder: (context, index) =>
|
||||
VSpace(GridSize.typeOptionSeparatorHeight),
|
||||
physics: StyledScrollPhysics(),
|
||||
itemBuilder: (BuildContext context, int index) => items[index],
|
||||
padding: const EdgeInsets.all(6.0),
|
||||
),
|
||||
);
|
||||
return BlocProvider(
|
||||
create: (context) {
|
||||
return CalendarSettingBloc(
|
||||
viewId: widget.viewId,
|
||||
layoutSettings: widget.calendarSettingController.getLayoutSetting(),
|
||||
)..add(
|
||||
const CalendarSettingEvent.init(),
|
||||
);
|
||||
},
|
||||
child: BlocBuilder<CalendarSettingBloc, CalendarSettingState>(
|
||||
builder: (context, state) {
|
||||
final CalendarLayoutSettingPB? settings = state.layoutSetting
|
||||
.foldLeft(null, (previous, settings) => settings);
|
||||
|
||||
if (settings == null) {
|
||||
return const CircularProgressIndicator();
|
||||
}
|
||||
final availableSettings = _availableCalendarSettings(settings);
|
||||
final items = availableSettings.map((setting) {
|
||||
switch (setting) {
|
||||
case CalendarLayoutSettingAction.showWeekNumber:
|
||||
return ShowWeekNumber(
|
||||
showWeekNumbers: settings.showWeekNumbers,
|
||||
onUpdated: (showWeekNumbers) {
|
||||
_updateLayoutSettings(
|
||||
context,
|
||||
showWeekNumbers: showWeekNumbers,
|
||||
);
|
||||
},
|
||||
);
|
||||
case CalendarLayoutSettingAction.showWeekends:
|
||||
return ShowWeekends(
|
||||
showWeekends: settings.showWeekends,
|
||||
onUpdated: (showWeekends) {
|
||||
_updateLayoutSettings(
|
||||
context,
|
||||
showWeekends: showWeekends,
|
||||
);
|
||||
},
|
||||
);
|
||||
case CalendarLayoutSettingAction.firstDayOfWeek:
|
||||
return FirstDayOfWeek(
|
||||
firstDayOfWeek: settings.firstDayOfWeek,
|
||||
popoverMutex: popoverMutex,
|
||||
onUpdated: (firstDayOfWeek) {
|
||||
_updateLayoutSettings(
|
||||
context,
|
||||
firstDayOfWeek: firstDayOfWeek,
|
||||
);
|
||||
},
|
||||
);
|
||||
case CalendarLayoutSettingAction.layoutField:
|
||||
return LayoutDateField(
|
||||
fieldController: widget.fieldController,
|
||||
viewId: widget.viewId,
|
||||
fieldId: settings.fieldId,
|
||||
popoverMutex: popoverMutex,
|
||||
onUpdated: (fieldId) {
|
||||
_updateLayoutSettings(
|
||||
context,
|
||||
layoutFieldId: fieldId,
|
||||
);
|
||||
},
|
||||
);
|
||||
default:
|
||||
return const SizedBox();
|
||||
}
|
||||
}).toList();
|
||||
|
||||
return SizedBox(
|
||||
width: 200,
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
controller: ScrollController(),
|
||||
itemCount: items.length,
|
||||
separatorBuilder: (context, index) =>
|
||||
VSpace(GridSize.typeOptionSeparatorHeight),
|
||||
physics: StyledScrollPhysics(),
|
||||
itemBuilder: (BuildContext context, int index) => items[index],
|
||||
padding: const EdgeInsets.all(6.0),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -161,7 +174,6 @@ class _CalendarLayoutSettingState extends State<CalendarLayoutSetting> {
|
||||
|
||||
void _updateLayoutSettings(
|
||||
BuildContext context, {
|
||||
required Function(CalendarLayoutSettingPB? layoutSettings) onUpdated,
|
||||
bool? showWeekends,
|
||||
bool? showWeekNumbers,
|
||||
int? firstDayOfWeek,
|
||||
@@ -190,7 +202,8 @@ class _CalendarLayoutSettingState extends State<CalendarLayoutSetting> {
|
||||
context
|
||||
.read<CalendarSettingBloc>()
|
||||
.add(CalendarSettingEvent.updateLayoutSetting(setting));
|
||||
onUpdated(setting);
|
||||
|
||||
widget.calendarSettingController.updateLayoutSettings(setting);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-137
@@ -1,137 +0,0 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/calendar/application/calendar_setting_bloc.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/field/grid_property.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
import 'calendar_layout_setting.dart';
|
||||
|
||||
/// The highest-level widget shown in the popover triggered by clicking the
|
||||
/// "Settings" button. Shows [AllCalendarSettings] by default, but replaces its
|
||||
/// contents with the submenu when a category is selected.
|
||||
class CalendarSetting extends StatelessWidget {
|
||||
final CalendarSettingContext settingContext;
|
||||
final CalendarLayoutSettingPB? layoutSettings;
|
||||
final Function(CalendarLayoutSettingPB? layoutSettings) onUpdated;
|
||||
|
||||
const CalendarSetting({
|
||||
required this.onUpdated,
|
||||
required this.layoutSettings,
|
||||
required this.settingContext,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider<CalendarSettingBloc>(
|
||||
create: (context) => CalendarSettingBloc(layoutSettings: layoutSettings),
|
||||
child: BlocBuilder<CalendarSettingBloc, CalendarSettingState>(
|
||||
builder: (context, state) {
|
||||
final CalendarSettingAction? action =
|
||||
state.selectedAction.foldLeft(null, (previous, action) => action);
|
||||
switch (action) {
|
||||
case CalendarSettingAction.properties:
|
||||
return DatabasePropertyList(
|
||||
viewId: settingContext.viewId,
|
||||
fieldController: settingContext.fieldController,
|
||||
);
|
||||
case CalendarSettingAction.layout:
|
||||
return CalendarLayoutSetting(
|
||||
onUpdated: onUpdated,
|
||||
settingContext: settingContext,
|
||||
);
|
||||
default:
|
||||
return const AllCalendarSettings().padding(all: 6.0);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Shows all of the available categories of settings that can be set here.
|
||||
/// For now, this only includes the Layout category.
|
||||
class AllCalendarSettings extends StatelessWidget {
|
||||
const AllCalendarSettings({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final items = CalendarSettingAction.values
|
||||
.map((e) => _settingItem(context, e))
|
||||
.toList();
|
||||
|
||||
return SizedBox(
|
||||
width: 140,
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
controller: ScrollController(),
|
||||
itemCount: items.length,
|
||||
separatorBuilder: (context, index) =>
|
||||
VSpace(GridSize.typeOptionSeparatorHeight),
|
||||
physics: StyledScrollPhysics(),
|
||||
itemBuilder: (BuildContext context, int index) => items[index],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _settingItem(BuildContext context, CalendarSettingAction action) {
|
||||
Widget? icon;
|
||||
if (action.iconName() != null) {
|
||||
icon = FlowySvg(
|
||||
name: action.iconName()!,
|
||||
);
|
||||
}
|
||||
return SizedBox(
|
||||
height: GridSize.popoverItemHeight,
|
||||
child: FlowyButton(
|
||||
leftIcon: icon,
|
||||
text: FlowyText.medium(action.title()),
|
||||
onTap: () {
|
||||
context
|
||||
.read<CalendarSettingBloc>()
|
||||
.add(CalendarSettingEvent.performAction(action));
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension _SettingExtension on CalendarSettingAction {
|
||||
String? iconName() {
|
||||
switch (this) {
|
||||
case CalendarSettingAction.properties:
|
||||
return 'grid/setting/properties';
|
||||
case CalendarSettingAction.layout:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String title() {
|
||||
switch (this) {
|
||||
case CalendarSettingAction.properties:
|
||||
return LocaleKeys.grid_settings_Properties.tr();
|
||||
case CalendarSettingAction.layout:
|
||||
return LocaleKeys.grid_settings_layout.tr();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CalendarSettingContext {
|
||||
final String viewId;
|
||||
final FieldController fieldController;
|
||||
|
||||
CalendarSettingContext({
|
||||
required this.viewId,
|
||||
required this.fieldController,
|
||||
});
|
||||
}
|
||||
-47
@@ -11,7 +11,6 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../application/calendar_bloc.dart';
|
||||
import 'calendar_setting.dart';
|
||||
|
||||
class CalendarToolbar extends StatelessWidget {
|
||||
const CalendarToolbar({super.key});
|
||||
@@ -33,52 +32,6 @@ class CalendarToolbar extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _SettingButton extends StatefulWidget {
|
||||
const _SettingButton({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _SettingButtonState();
|
||||
}
|
||||
|
||||
class _SettingButtonState extends State<_SettingButton> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppFlowyPopover(
|
||||
direction: PopoverDirection.bottomWithRightAligned,
|
||||
constraints: BoxConstraints.loose(const Size(300, 400)),
|
||||
margin: EdgeInsets.zero,
|
||||
child: FlowyTextButton(
|
||||
LocaleKeys.settings_title.tr(),
|
||||
fillColor: Colors.transparent,
|
||||
hoverColor: AFThemeExtension.of(context).lightGreyHover,
|
||||
padding: GridSize.typeOptionContentInsets,
|
||||
),
|
||||
popupBuilder: (BuildContext popoverContext) {
|
||||
final bloc = context.watch<CalendarBloc>();
|
||||
final settingContext = CalendarSettingContext(
|
||||
viewId: bloc.viewId,
|
||||
fieldController: bloc.fieldController,
|
||||
);
|
||||
return CalendarSetting(
|
||||
settingContext: settingContext,
|
||||
layoutSettings: bloc.state.settings.fold(
|
||||
() => null,
|
||||
(settings) => settings,
|
||||
),
|
||||
onUpdated: (layoutSettings) {
|
||||
if (layoutSettings == null) {
|
||||
return;
|
||||
}
|
||||
context
|
||||
.read<CalendarBloc>()
|
||||
.add(CalendarEvent.updateCalendarLayoutSetting(layoutSettings));
|
||||
},
|
||||
);
|
||||
}, // use blocbuilder
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _UnscheduleEventsButton extends StatefulWidget {
|
||||
const _UnscheduleEventsButton({Key? key}) : super(key: key);
|
||||
|
||||
|
||||
@@ -31,6 +31,12 @@ class DatabaseViewPlugin extends Plugin {
|
||||
@override
|
||||
PluginWidgetBuilder get widgetBuilder => _innerPlugin.widgetBuilder;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_viewListener.stop();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _listenOnLayoutChanged() {
|
||||
_viewListener.start(
|
||||
onViewUpdated: (result) {
|
||||
|
||||
@@ -69,11 +69,11 @@ class GridPluginWidgetBuilder extends PluginWidgetBuilder {
|
||||
Widget get leftBarItem => ViewLeftBarItem(view: view);
|
||||
|
||||
@override
|
||||
Widget buildWidget(PluginContext context) {
|
||||
Widget buildWidget({PluginContext? context}) {
|
||||
notifier.isDeleted.addListener(() {
|
||||
notifier.isDeleted.value.fold(() => null, (deletedView) {
|
||||
if (deletedView.hasIndex()) {
|
||||
context.onDeleted(view, deletedView.index);
|
||||
context?.onDeleted(view, deletedView.index);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
+4
-2
@@ -32,8 +32,10 @@ class _ChecklistCardCellState extends State<ChecklistCardCell> {
|
||||
return BlocProvider.value(
|
||||
value: _cellBloc,
|
||||
child: BlocBuilder<ChecklistCardCellBloc, ChecklistCellState>(
|
||||
builder: (context, state) =>
|
||||
ChecklistProgressBar(percent: state.percent),
|
||||
builder: (context, state) => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: ChecklistProgressBar(percent: state.percent),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import 'package:appflowy/startup/plugin/plugin.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_listener.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DatabaseViewWidget extends StatefulWidget {
|
||||
const DatabaseViewWidget({
|
||||
super.key,
|
||||
required this.view,
|
||||
});
|
||||
|
||||
final ViewPB view;
|
||||
|
||||
@override
|
||||
State<DatabaseViewWidget> createState() => _DatabaseViewWidgetState();
|
||||
}
|
||||
|
||||
class _DatabaseViewWidgetState extends State<DatabaseViewWidget> {
|
||||
/// Listens to the view updates.
|
||||
late final ViewListener _listener;
|
||||
|
||||
/// Notifies the view layout type changes. When the layout type changes,
|
||||
/// the widget of the view will be updated.
|
||||
late final ValueNotifier<ViewLayoutPB> _layoutTypeChangeNotifier;
|
||||
|
||||
/// The view will be updated by the [ViewListener].
|
||||
late ViewPB view;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
view = widget.view;
|
||||
_listenOnViewUpdated();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_layoutTypeChangeNotifier.dispose();
|
||||
_listener.stop();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder<ViewLayoutPB>(
|
||||
valueListenable: _layoutTypeChangeNotifier,
|
||||
builder: (_, __, ___) {
|
||||
return makePlugin(pluginType: view.pluginType, data: view)
|
||||
.widgetBuilder
|
||||
.buildWidget();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _listenOnViewUpdated() {
|
||||
_listener = ViewListener(view: widget.view)
|
||||
..start(
|
||||
onViewUpdated: (result) {
|
||||
result.fold(
|
||||
(updatedView) {
|
||||
if (mounted) {
|
||||
view = updatedView;
|
||||
_layoutTypeChangeNotifier.value = view.layout;
|
||||
}
|
||||
},
|
||||
(r) => null,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
_layoutTypeChangeNotifier = ValueNotifier(widget.view.layout);
|
||||
}
|
||||
}
|
||||
+13
-11
@@ -19,8 +19,8 @@ class GridCellAccessoryBuildContext {
|
||||
});
|
||||
}
|
||||
|
||||
class GridCellAccessoryBuilder {
|
||||
final GlobalKey _key = GlobalKey();
|
||||
class GridCellAccessoryBuilder<T extends State<StatefulWidget>> {
|
||||
final GlobalKey<T> _key = GlobalKey();
|
||||
|
||||
final Widget Function(Key key) _builder;
|
||||
|
||||
@@ -67,9 +67,16 @@ class _PrimaryCellAccessoryState extends State<PrimaryCellAccessory>
|
||||
Widget build(BuildContext context) {
|
||||
return Tooltip(
|
||||
message: LocaleKeys.tooltip_openAsPage.tr(),
|
||||
child: svgWidget(
|
||||
"grid/expander",
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
child: SizedBox(
|
||||
width: 26,
|
||||
height: 26,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(3.0),
|
||||
child: svgWidget(
|
||||
"grid/expander",
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -183,12 +190,7 @@ class CellAccessoryContainer extends StatelessWidget {
|
||||
final hover = FlowyHover(
|
||||
style:
|
||||
HoverStyle(hoverColor: AFThemeExtension.of(context).lightGreyHover),
|
||||
builder: (_, onHover) => Container(
|
||||
width: 26,
|
||||
height: 26,
|
||||
padding: const EdgeInsets.all(3),
|
||||
child: accessory.build(),
|
||||
),
|
||||
builder: (_, onHover) => accessory.build(),
|
||||
);
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
|
||||
+79
-33
@@ -1,15 +1,15 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/select_option.pb.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:textfield_tags/textfield_tags.dart';
|
||||
|
||||
import '../../../../grid/presentation/layout/sizes.dart';
|
||||
@@ -34,12 +34,14 @@ class SelectOptionCellEditor extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _SelectOptionCellEditorState extends State<SelectOptionCellEditor> {
|
||||
late PopoverMutex popoverMutex;
|
||||
final popoverMutex = PopoverMutex();
|
||||
final tagController = TextfieldTagsController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
popoverMutex = PopoverMutex();
|
||||
super.initState();
|
||||
void dispose() {
|
||||
popoverMutex.dispose();
|
||||
tagController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -53,9 +55,17 @@ class _SelectOptionCellEditorState extends State<SelectOptionCellEditor> {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_TextField(popoverMutex: popoverMutex),
|
||||
_TextField(
|
||||
popoverMutex: popoverMutex,
|
||||
tagController: tagController,
|
||||
),
|
||||
const TypeOptionSeparator(spacing: 0.0),
|
||||
Flexible(child: _OptionList(popoverMutex: popoverMutex)),
|
||||
Flexible(
|
||||
child: _OptionList(
|
||||
popoverMutex: popoverMutex,
|
||||
tagController: tagController,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
@@ -66,8 +76,11 @@ class _SelectOptionCellEditorState extends State<SelectOptionCellEditor> {
|
||||
|
||||
class _OptionList extends StatelessWidget {
|
||||
final PopoverMutex popoverMutex;
|
||||
final TextfieldTagsController tagController;
|
||||
|
||||
const _OptionList({
|
||||
required this.popoverMutex,
|
||||
required this.tagController,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@@ -75,17 +88,16 @@ class _OptionList extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SelectOptionCellEditorBloc, SelectOptionEditorState>(
|
||||
builder: (context, state) {
|
||||
final List<Widget> cells = [];
|
||||
cells.add(const _Title());
|
||||
cells.addAll(
|
||||
state.options.map((option) {
|
||||
return _SelectOptionCell(
|
||||
final cells = [
|
||||
_Title(onPressedAddButton: () => onPressedAddButton(context)),
|
||||
...state.options.map(
|
||||
(option) => _SelectOptionCell(
|
||||
option: option,
|
||||
isSelected: state.selectedOptions.contains(option),
|
||||
popoverMutex: popoverMutex,
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
state.createOption.fold(
|
||||
() => null,
|
||||
@@ -94,30 +106,38 @@ class _OptionList extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
|
||||
final list = ListView.separated(
|
||||
return ListView.separated(
|
||||
shrinkWrap: true,
|
||||
controller: ScrollController(),
|
||||
itemCount: cells.length,
|
||||
separatorBuilder: (context, index) {
|
||||
return VSpace(GridSize.typeOptionSeparatorHeight);
|
||||
},
|
||||
separatorBuilder: (_, __) =>
|
||||
VSpace(GridSize.typeOptionSeparatorHeight),
|
||||
physics: StyledScrollPhysics(),
|
||||
itemBuilder: (BuildContext context, int index) => cells[index],
|
||||
itemBuilder: (_, int index) => cells[index],
|
||||
padding: const EdgeInsets.only(top: 6.0, bottom: 12.0),
|
||||
);
|
||||
|
||||
return list;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void onPressedAddButton(BuildContext context) {
|
||||
final text = tagController.textEditingController?.text;
|
||||
if (text != null) {
|
||||
context.read<SelectOptionCellEditorBloc>().add(
|
||||
SelectOptionEditorEvent.trySelectOption(text),
|
||||
);
|
||||
}
|
||||
tagController.textEditingController?.clear();
|
||||
}
|
||||
}
|
||||
|
||||
class _TextField extends StatelessWidget {
|
||||
final PopoverMutex popoverMutex;
|
||||
final TextfieldTagsController _tagController = TextfieldTagsController();
|
||||
final TextfieldTagsController tagController;
|
||||
|
||||
_TextField({
|
||||
const _TextField({
|
||||
required this.popoverMutex,
|
||||
required this.tagController,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@@ -138,7 +158,7 @@ class _TextField extends StatelessWidget {
|
||||
selectedOptionMap: optionMap,
|
||||
distanceToText: _editorPanelWidth * 0.7,
|
||||
maxLength: 30,
|
||||
tagController: _tagController,
|
||||
tagController: tagController,
|
||||
textSeparators: const [','],
|
||||
onClick: () => popoverMutex.close(),
|
||||
newText: (text) {
|
||||
@@ -174,7 +194,11 @@ class _TextField extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _Title extends StatelessWidget {
|
||||
const _Title({Key? key}) : super(key: key);
|
||||
const _Title({
|
||||
required this.onPressedAddButton,
|
||||
});
|
||||
|
||||
final VoidCallback onPressedAddButton;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -182,9 +206,27 @@ class _Title extends StatelessWidget {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: SizedBox(
|
||||
height: GridSize.popoverItemHeight,
|
||||
child: FlowyText.medium(
|
||||
LocaleKeys.grid_selectOption_panelTitle.tr(),
|
||||
color: Theme.of(context).hintColor,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
FlowyText.medium(
|
||||
LocaleKeys.grid_selectOption_panelTitle.tr(),
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4.0,
|
||||
),
|
||||
child: FlowyIconButton(
|
||||
onPressed: onPressedAddButton,
|
||||
width: 18,
|
||||
icon: svgWidget(
|
||||
'home/add',
|
||||
color: Theme.of(context).iconTheme.color,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -192,8 +234,11 @@ class _Title extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _CreateOptionCell extends StatelessWidget {
|
||||
const _CreateOptionCell({
|
||||
required this.name,
|
||||
});
|
||||
|
||||
final String name;
|
||||
const _CreateOptionCell({required this.name, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -231,6 +276,7 @@ class _SelectOptionCell extends StatefulWidget {
|
||||
final SelectOptionPB option;
|
||||
final PopoverMutex popoverMutex;
|
||||
final bool isSelected;
|
||||
|
||||
const _SelectOptionCell({
|
||||
required this.option,
|
||||
required this.isSelected,
|
||||
|
||||
+93
-48
@@ -1,5 +1,6 @@
|
||||
import 'dart:async';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
@@ -35,12 +36,14 @@ enum GridURLCellAccessoryType {
|
||||
visitURL,
|
||||
}
|
||||
|
||||
typedef URLCellDataNotifier = CellDataNotifier<String>;
|
||||
|
||||
class GridURLCell extends GridCellWidget {
|
||||
GridURLCell({
|
||||
super.key,
|
||||
required this.cellControllerBuilder,
|
||||
GridCellStyle? style,
|
||||
}) {
|
||||
}) : _cellDataNotifier = CellDataNotifier(value: '') {
|
||||
if (style != null) {
|
||||
cellStyle = (style as GridURLCellStyle);
|
||||
} else {
|
||||
@@ -48,36 +51,14 @@ class GridURLCell extends GridCellWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// Use
|
||||
final URLCellDataNotifier _cellDataNotifier;
|
||||
final CellControllerBuilder cellControllerBuilder;
|
||||
late final GridURLCellStyle? cellStyle;
|
||||
|
||||
@override
|
||||
GridCellState<GridURLCell> createState() => _GridURLCellState();
|
||||
|
||||
GridCellAccessoryBuilder accessoryFromType(
|
||||
GridURLCellAccessoryType ty,
|
||||
GridCellAccessoryBuildContext buildContext,
|
||||
) {
|
||||
switch (ty) {
|
||||
case GridURLCellAccessoryType.visitURL:
|
||||
final cellContext = cellControllerBuilder.build() as URLCellController;
|
||||
return GridCellAccessoryBuilder(
|
||||
builder: (Key key) => _VisitURLAccessory(
|
||||
key: key,
|
||||
cellContext: cellContext,
|
||||
),
|
||||
);
|
||||
case GridURLCellAccessoryType.copyURL:
|
||||
final cellContext = cellControllerBuilder.build() as URLCellController;
|
||||
return GridCellAccessoryBuilder(
|
||||
builder: (Key key) => _CopyURLAccessory(
|
||||
key: key,
|
||||
cellContext: cellContext,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
List<GridCellAccessoryBuilder> Function(
|
||||
GridCellAccessoryBuildContext buildContext,
|
||||
@@ -86,7 +67,7 @@ class GridURLCell extends GridCellWidget {
|
||||
if (cellStyle != null) {
|
||||
accessories.addAll(
|
||||
cellStyle!.accessoryTypes.map((ty) {
|
||||
return accessoryFromType(ty, buildContext);
|
||||
return _accessoryFromType(ty, buildContext);
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -94,7 +75,7 @@ class GridURLCell extends GridCellWidget {
|
||||
// If the accessories is empty then the default accessory will be GridURLCellAccessoryType.visitURL
|
||||
if (accessories.isEmpty) {
|
||||
accessories.add(
|
||||
accessoryFromType(
|
||||
_accessoryFromType(
|
||||
GridURLCellAccessoryType.visitURL,
|
||||
buildContext,
|
||||
),
|
||||
@@ -103,6 +84,28 @@ class GridURLCell extends GridCellWidget {
|
||||
|
||||
return accessories;
|
||||
};
|
||||
|
||||
GridCellAccessoryBuilder _accessoryFromType(
|
||||
GridURLCellAccessoryType ty,
|
||||
GridCellAccessoryBuildContext buildContext,
|
||||
) {
|
||||
switch (ty) {
|
||||
case GridURLCellAccessoryType.visitURL:
|
||||
return VisitURLCellAccessoryBuilder(
|
||||
builder: (Key key) => _VisitURLAccessory(
|
||||
key: key,
|
||||
cellDataNotifier: _cellDataNotifier,
|
||||
),
|
||||
);
|
||||
case GridURLCellAccessoryType.copyURL:
|
||||
return CopyURLCellAccessoryBuilder(
|
||||
builder: (Key key) => _CopyURLAccessory(
|
||||
key: key,
|
||||
cellDataNotifier: _cellDataNotifier,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _GridURLCellState extends GridFocusNodeCellState<GridURLCell> {
|
||||
@@ -133,8 +136,11 @@ class _GridURLCellState extends GridFocusNodeCellState<GridURLCell> {
|
||||
value: _cellBloc,
|
||||
child: BlocConsumer<URLCellBloc, URLCellState>(
|
||||
listenWhen: (previous, current) => previous.content != current.content,
|
||||
listener: (context, state) => _controller.text = state.content,
|
||||
listener: (context, state) {
|
||||
_controller.text = state.content;
|
||||
},
|
||||
builder: (context, state) {
|
||||
widget._cellDataNotifier.value = state.content;
|
||||
final urlEditor = Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: GridSize.cellContentInsets.left,
|
||||
@@ -232,33 +238,41 @@ class _EditURLAccessoryState extends State<_EditURLAccessory>
|
||||
}
|
||||
}
|
||||
|
||||
typedef CopyURLCellAccessoryBuilder
|
||||
= GridCellAccessoryBuilder<State<_CopyURLAccessory>>;
|
||||
|
||||
class _CopyURLAccessory extends StatefulWidget {
|
||||
const _CopyURLAccessory({
|
||||
super.key,
|
||||
required this.cellContext,
|
||||
required this.cellDataNotifier,
|
||||
});
|
||||
|
||||
final URLCellController cellContext;
|
||||
final URLCellDataNotifier cellDataNotifier;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _CopyURLAccessoryState();
|
||||
State<_CopyURLAccessory> createState() => _CopyURLAccessoryState();
|
||||
}
|
||||
|
||||
class _CopyURLAccessoryState extends State<_CopyURLAccessory>
|
||||
with GridCellAccessoryState {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return svgWidget(
|
||||
"editor/copy",
|
||||
color: AFThemeExtension.of(context).textColor,
|
||||
);
|
||||
if (widget.cellDataNotifier.value.isNotEmpty) {
|
||||
return _URLAccessoryIconContainer(
|
||||
child: svgWidget(
|
||||
"editor/copy",
|
||||
color: AFThemeExtension.of(context).textColor,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onTap() {
|
||||
final content =
|
||||
widget.cellContext.getCellData(loadIfNotExist: false)?.content;
|
||||
if (content == null) {
|
||||
final content = widget.cellDataNotifier.value;
|
||||
if (content.isEmpty) {
|
||||
return;
|
||||
}
|
||||
Clipboard.setData(ClipboardData(text: content));
|
||||
@@ -266,33 +280,46 @@ class _CopyURLAccessoryState extends State<_CopyURLAccessory>
|
||||
}
|
||||
}
|
||||
|
||||
typedef VisitURLCellAccessoryBuilder
|
||||
= GridCellAccessoryBuilder<State<_VisitURLAccessory>>;
|
||||
|
||||
class _VisitURLAccessory extends StatefulWidget {
|
||||
const _VisitURLAccessory({
|
||||
super.key,
|
||||
required this.cellContext,
|
||||
required this.cellDataNotifier,
|
||||
});
|
||||
|
||||
final URLCellController cellContext;
|
||||
final URLCellDataNotifier cellDataNotifier;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _VisitURLAccessoryState();
|
||||
State<_VisitURLAccessory> createState() => _VisitURLAccessoryState();
|
||||
}
|
||||
|
||||
class _VisitURLAccessoryState extends State<_VisitURLAccessory>
|
||||
with GridCellAccessoryState {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return svgWidget(
|
||||
"editor/link",
|
||||
color: AFThemeExtension.of(context).textColor,
|
||||
);
|
||||
if (widget.cellDataNotifier.value.isNotEmpty) {
|
||||
return _URLAccessoryIconContainer(
|
||||
child: svgWidget(
|
||||
"editor/link",
|
||||
color: AFThemeExtension.of(context).textColor,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool enable() {
|
||||
return widget.cellDataNotifier.value.isNotEmpty;
|
||||
}
|
||||
|
||||
@override
|
||||
void onTap() {
|
||||
final content =
|
||||
widget.cellContext.getCellData(loadIfNotExist: false)?.content;
|
||||
if (content == null) {
|
||||
final content = widget.cellDataNotifier.value;
|
||||
if (content.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final shouldAddScheme =
|
||||
@@ -301,3 +328,21 @@ class _VisitURLAccessoryState extends State<_VisitURLAccessory>
|
||||
canLaunchUrlString(url).then((value) => launchUrlString(url));
|
||||
}
|
||||
}
|
||||
|
||||
class _URLAccessoryIconContainer extends StatelessWidget {
|
||||
const _URLAccessoryIconContainer({required this.child});
|
||||
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 26,
|
||||
height: 26,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(3.0),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+14
-23
@@ -1,6 +1,5 @@
|
||||
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/setting/setting_bloc.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
|
||||
import 'package:flowy_infra/image.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
@@ -9,7 +8,7 @@ import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../layout/sizes.dart';
|
||||
import '../../grid/presentation/layout/sizes.dart';
|
||||
|
||||
class DatabaseSettingList extends StatelessWidget {
|
||||
final DatabaseController databaseContoller;
|
||||
@@ -22,33 +21,25 @@ class DatabaseSettingList extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final cells = DatabaseSettingAction.values.where((element) {
|
||||
if (element == DatabaseSettingAction.showGroup) {
|
||||
return databaseContoller.databaseLayout == DatabaseLayoutPB.Board;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}).map((action) {
|
||||
final cells = actionsForDatabaseLayout(databaseContoller.databaseLayout)
|
||||
.map((action) {
|
||||
return _SettingItem(
|
||||
action: action,
|
||||
onAction: (action) => onAction(action, databaseContoller),
|
||||
);
|
||||
}).toList();
|
||||
|
||||
return SizedBox(
|
||||
width: 140,
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
controller: ScrollController(),
|
||||
itemCount: cells.length,
|
||||
separatorBuilder: (context, index) {
|
||||
return VSpace(GridSize.typeOptionSeparatorHeight);
|
||||
},
|
||||
physics: StyledScrollPhysics(),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return cells[index];
|
||||
},
|
||||
),
|
||||
return ListView.separated(
|
||||
shrinkWrap: true,
|
||||
controller: ScrollController(),
|
||||
itemCount: cells.length,
|
||||
separatorBuilder: (context, index) {
|
||||
return VSpace(GridSize.typeOptionSeparatorHeight);
|
||||
},
|
||||
physics: StyledScrollPhysics(),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return cells[index];
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
+27
-1
@@ -1,7 +1,9 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/database_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/setting/setting_bloc.dart';
|
||||
import 'package:appflowy/plugins/database_view/calendar/presentation/toolbar/calendar_layout_setting.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/group/database_group.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/calendar_entities.pb.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
@@ -12,7 +14,7 @@ import 'package:styled_widget/styled_widget.dart';
|
||||
import '../../grid/presentation/layout/sizes.dart';
|
||||
import '../../grid/presentation/widgets/toolbar/grid_layout.dart';
|
||||
import '../field/grid_property.dart';
|
||||
import '../../grid/presentation/widgets/toolbar/grid_setting.dart';
|
||||
import 'database_setting.dart';
|
||||
|
||||
class SettingButton extends StatefulWidget {
|
||||
final DatabaseController databaseController;
|
||||
@@ -110,7 +112,31 @@ class _DatabaseSettingListPopoverState
|
||||
viewId: widget.databaseController.viewId,
|
||||
fieldController: widget.databaseController.fieldController,
|
||||
);
|
||||
case DatabaseSettingAction.showCalendarLayout:
|
||||
return CalendarLayoutSetting(
|
||||
viewId: widget.databaseController.viewId,
|
||||
fieldController: widget.databaseController.fieldController,
|
||||
calendarSettingController: ICalendarSettingImpl(
|
||||
widget.databaseController,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ICalendarSettingImpl extends ICalendarSetting {
|
||||
final DatabaseController _databaseController;
|
||||
|
||||
ICalendarSettingImpl(this._databaseController);
|
||||
|
||||
@override
|
||||
void updateLayoutSettings(CalendarLayoutSettingPB layoutSettings) {
|
||||
_databaseController.updateCalenderLayoutSetting(layoutSettings);
|
||||
}
|
||||
|
||||
@override
|
||||
CalendarLayoutSettingPB? getLayoutSetting() {
|
||||
return _databaseController.databaseLayoutSetting?.calendar;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
export 'doc_bloc.dart';
|
||||
export 'doc_service.dart';
|
||||
export 'share_bloc.dart';
|
||||
export 'share_service.dart';
|
||||
@@ -1,65 +1,46 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:appflowy/plugins/document/application/share_service.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/divider_node_parser.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/math_equation_node_parser.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/code_block_node_parser.dart';
|
||||
import 'package:appflowy/workspace/application/export/document_exporter.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-document2/entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart'
|
||||
show Document, documentToMarkdown;
|
||||
part 'share_bloc.freezed.dart';
|
||||
|
||||
class DocShareBloc extends Bloc<DocShareEvent, DocShareState> {
|
||||
ShareService service;
|
||||
ViewPB view;
|
||||
DocShareBloc({required this.view, required this.service})
|
||||
: super(const DocShareState.initial()) {
|
||||
on<DocShareEvent>((event, emit) async {
|
||||
await event.map(
|
||||
shareMarkdown: (ShareMarkdown shareMarkdown) async {
|
||||
await service.exportMarkdown(view).then((result) {
|
||||
result.fold(
|
||||
(value) => emit(
|
||||
DocShareState.finish(
|
||||
left(_saveMarkdown(value, shareMarkdown.path)),
|
||||
),
|
||||
),
|
||||
(error) => emit(DocShareState.finish(right(error))),
|
||||
);
|
||||
});
|
||||
|
||||
emit(const DocShareState.loading());
|
||||
},
|
||||
shareLink: (ShareLink value) {},
|
||||
shareText: (ShareText value) {},
|
||||
);
|
||||
});
|
||||
DocShareBloc({
|
||||
required this.view,
|
||||
}) : super(const DocShareState.initial()) {
|
||||
on<ShareMarkdown>(_onShareMarkdown);
|
||||
}
|
||||
|
||||
ExportDataPB _saveMarkdown(ExportDataPB value, String path) {
|
||||
final markdown = _convertDocumentToMarkdown(value);
|
||||
value.data = markdown;
|
||||
File(path).writeAsStringSync(markdown);
|
||||
return value;
|
||||
}
|
||||
final ViewPB view;
|
||||
|
||||
String _convertDocumentToMarkdown(ExportDataPB value) {
|
||||
final json = jsonDecode(value.data);
|
||||
final document = Document.fromJson(json);
|
||||
return documentToMarkdown(
|
||||
document,
|
||||
customParsers: [
|
||||
const DividerNodeParser(),
|
||||
const MathEquationNodeParser(),
|
||||
const CodeBlockNodeParser(),
|
||||
],
|
||||
Future<void> _onShareMarkdown(
|
||||
ShareMarkdown event,
|
||||
Emitter<DocShareState> emit,
|
||||
) async {
|
||||
emit(const DocShareState.loading());
|
||||
|
||||
final documentExporter = DocumentExporter(view);
|
||||
final result = await documentExporter.export(DocumentExportType.markdown);
|
||||
emit(
|
||||
DocShareState.finish(
|
||||
result.fold(
|
||||
(error) => right(error),
|
||||
(markdown) => left(_saveMarkdownToPath(markdown, event.path)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
ExportDataPB _saveMarkdownToPath(String markdown, String path) {
|
||||
File(path).writeAsStringSync(markdown);
|
||||
return ExportDataPB()
|
||||
..data = markdown
|
||||
..exportType = ExportType.Markdown;
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'package:appflowy_backend/protobuf/flowy-document2/entities.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
|
||||
class ShareService {
|
||||
Future<Either<ExportDataPB, FlowyError>> export(
|
||||
ViewPB view,
|
||||
ExportType type,
|
||||
) {
|
||||
// var payload = ExportPayloadPB.create()
|
||||
// ..viewId = view.id
|
||||
// ..exportType = type
|
||||
// ..documentVersion = DocumentVersionPB.V1;
|
||||
|
||||
// return DocumentEventExportDocument(payload).send();
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
Future<Either<ExportDataPB, FlowyError>> exportText(ViewPB view) {
|
||||
return export(view, ExportType.Text);
|
||||
}
|
||||
|
||||
Future<Either<ExportDataPB, FlowyError>> exportMarkdown(ViewPB view) {
|
||||
return export(view, ExportType.Markdown);
|
||||
}
|
||||
|
||||
Future<Either<ExportDataPB, FlowyError>> exportURL(ViewPB view) {
|
||||
return export(view, ExportType.Link);
|
||||
}
|
||||
}
|
||||
@@ -92,7 +92,7 @@ class DocumentPluginWidgetBuilder extends PluginWidgetBuilder
|
||||
EdgeInsets get contentPadding => EdgeInsets.zero;
|
||||
|
||||
@override
|
||||
Widget buildWidget(PluginContext context) {
|
||||
Widget buildWidget({PluginContext? context}) {
|
||||
notifier.isDeleted.addListener(() {
|
||||
notifier.isDeleted.value.fold(() => null, (deletedView) {
|
||||
if (deletedView.hasIndex()) {
|
||||
@@ -107,7 +107,7 @@ class DocumentPluginWidgetBuilder extends PluginWidgetBuilder
|
||||
builder: (_, state) {
|
||||
return DocumentPage(
|
||||
view: view,
|
||||
onDeleted: () => context.onDeleted(view, deletedViewIndex),
|
||||
onDeleted: () => context?.onDeleted(view, deletedViewIndex),
|
||||
key: ValueKey(view.id),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:appflowy/plugins/document/application/doc_bloc.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/option_action.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_list.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/database/referenced_database_menu_tem.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
@@ -45,9 +46,11 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
|
||||
late final slashMenuItems = [
|
||||
inlineGridMenuItem(documentBloc),
|
||||
referenceGridMenuItem,
|
||||
referencedGridMenuItem,
|
||||
inlineBoardMenuItem(documentBloc),
|
||||
boardMenuItem,
|
||||
referencedBoardMenuItem,
|
||||
inlineCalendarMenuItem(documentBloc),
|
||||
referencedCalendarMenuItem,
|
||||
calloutItem,
|
||||
mathEquationItem,
|
||||
codeBlockItem,
|
||||
@@ -174,10 +177,13 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
ImageBlockKeys.type: ImageBlockComponentBuilder(
|
||||
configuration: configuration,
|
||||
),
|
||||
BoardBlockKeys.type: BoardBlockComponentBuilder(
|
||||
DatabaseBlockKeys.gridType: DatabaseViewBlockComponentBuilder(
|
||||
configuration: configuration,
|
||||
),
|
||||
GridBlockKeys.type: GridBlockComponentBuilder(
|
||||
DatabaseBlockKeys.boardType: DatabaseViewBlockComponentBuilder(
|
||||
configuration: configuration,
|
||||
),
|
||||
DatabaseBlockKeys.calendarType: DatabaseViewBlockComponentBuilder(
|
||||
configuration: configuration,
|
||||
),
|
||||
CalloutBlockKeys.type: CalloutBlockComponentBuilder(
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/insert_page_command.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
|
||||
+7
-11
@@ -1,19 +1,11 @@
|
||||
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/editor_plugins/board/board_node_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/grid/grid_node_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
class DatabaseBlockKeys {
|
||||
const DatabaseBlockKeys._();
|
||||
|
||||
static const String parentID = 'parent_id';
|
||||
static const String viewID = 'view_id';
|
||||
}
|
||||
|
||||
extension InsertDatabase on EditorState {
|
||||
Future<void> insertInlinePage(String parentViewId, ViewPB childView) async {
|
||||
final selection = this.selection;
|
||||
@@ -93,6 +85,8 @@ extension InsertDatabase on EditorState {
|
||||
return LocaleKeys.grid_referencedGridPrefix.tr();
|
||||
case ViewLayoutPB.Board:
|
||||
return LocaleKeys.board_referencedBoardPrefix.tr();
|
||||
case ViewLayoutPB.Calendar:
|
||||
return LocaleKeys.calendar_referencedCalendarPrefix.tr();
|
||||
default:
|
||||
throw UnimplementedError();
|
||||
}
|
||||
@@ -101,9 +95,11 @@ extension InsertDatabase on EditorState {
|
||||
String _convertPageType(ViewPB viewPB) {
|
||||
switch (viewPB.layout) {
|
||||
case ViewLayoutPB.Grid:
|
||||
return GridBlockKeys.type;
|
||||
return DatabaseBlockKeys.gridType;
|
||||
case ViewLayoutPB.Board:
|
||||
return BoardBlockKeys.type;
|
||||
return DatabaseBlockKeys.boardType;
|
||||
case ViewLayoutPB.Calendar:
|
||||
return DatabaseBlockKeys.calendarType;
|
||||
default:
|
||||
throw Exception('Unknown layout type');
|
||||
}
|
||||
|
||||
+6
@@ -243,6 +243,8 @@ class _LinkToPageMenuState extends State<LinkToPageMenu> {
|
||||
return 'editor/grid';
|
||||
case ViewLayoutPB.Board:
|
||||
return 'editor/board';
|
||||
case ViewLayoutPB.Calendar:
|
||||
return 'editor/calendar';
|
||||
default:
|
||||
throw Exception('Unknown layout type');
|
||||
}
|
||||
@@ -258,6 +260,10 @@ extension on ViewLayoutPB {
|
||||
case ViewLayoutPB.Board:
|
||||
return LocaleKeys.document_slashMenu_board_selectABoardToLinkTo.tr();
|
||||
|
||||
case ViewLayoutPB.Calendar:
|
||||
return LocaleKeys.document_slashMenu_calendar_selectACalendarToLinkTo
|
||||
.tr();
|
||||
|
||||
default:
|
||||
throw Exception('Unknown layout type');
|
||||
}
|
||||
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/link_to_page_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/selectable_svg_widget.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
SelectionMenuItem boardMenuItem = SelectionMenuItem(
|
||||
name: LocaleKeys.document_plugins_referencedBoard.tr(),
|
||||
icon: (editorState, onSelected, style) => SelectableSvgWidget(
|
||||
name: 'editor/board',
|
||||
isSelected: onSelected,
|
||||
style: style,
|
||||
),
|
||||
keywords: ['referenced', 'board', 'kanban'],
|
||||
handler: (editorState, menuService, context) {
|
||||
final container = Overlay.of(context);
|
||||
showLinkToPageMenu(
|
||||
container,
|
||||
editorState,
|
||||
menuService,
|
||||
ViewLayoutPB.Board,
|
||||
);
|
||||
},
|
||||
);
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/selectable_svg_widget.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/application/prelude.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/insert_page_command.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
SelectionMenuItem inlineBoardMenuItem(DocumentBloc documentBloc) =>
|
||||
SelectionMenuItem(
|
||||
name: LocaleKeys.document_slashMenu_board_createANewBoard.tr(),
|
||||
icon: (editorState, onSelected, style) => SelectableSvgWidget(
|
||||
name: 'editor/board',
|
||||
isSelected: onSelected,
|
||||
style: style,
|
||||
),
|
||||
keywords: ['board', 'kanban', 'database'],
|
||||
handler: (editorState, menuService, context) async {
|
||||
if (!documentBloc.view.hasParentViewId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final parentViewId = documentBloc.view.parentViewId;
|
||||
ViewBackendService.createView(
|
||||
parentViewId: parentViewId,
|
||||
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
layoutType: ViewLayoutPB.Board,
|
||||
).then(
|
||||
(value) => value
|
||||
.swap()
|
||||
.map((r) => editorState.insertInlinePage(parentViewId, r)),
|
||||
);
|
||||
},
|
||||
);
|
||||
+19
-14
@@ -1,18 +1,22 @@
|
||||
import 'package:appflowy/plugins/database_view/board/presentation/board_page.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/database_view_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/built_in_page_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/insert_page_command.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class BoardBlockKeys {
|
||||
const BoardBlockKeys._();
|
||||
class DatabaseBlockKeys {
|
||||
const DatabaseBlockKeys._();
|
||||
|
||||
static const String type = 'board';
|
||||
static const String gridType = 'grid';
|
||||
static const String boardType = 'board';
|
||||
static const String calendarType = 'calendar';
|
||||
|
||||
static const String parentID = 'parent_id';
|
||||
static const String viewID = 'view_id';
|
||||
}
|
||||
|
||||
class BoardBlockComponentBuilder extends BlockComponentBuilder {
|
||||
BoardBlockComponentBuilder({
|
||||
class DatabaseViewBlockComponentBuilder extends BlockComponentBuilder {
|
||||
DatabaseViewBlockComponentBuilder({
|
||||
this.configuration = const BlockComponentConfiguration(),
|
||||
});
|
||||
|
||||
@@ -22,7 +26,7 @@ class BoardBlockComponentBuilder extends BlockComponentBuilder {
|
||||
@override
|
||||
BlockComponentWidget build(BlockComponentContext blockComponentContext) {
|
||||
final node = blockComponentContext.node;
|
||||
return BoardBlockComponentWidget(
|
||||
return DatabaseBlockComponentWidget(
|
||||
key: node.key,
|
||||
node: node,
|
||||
configuration: configuration,
|
||||
@@ -41,8 +45,8 @@ class BoardBlockComponentBuilder extends BlockComponentBuilder {
|
||||
node.attributes[DatabaseBlockKeys.viewID] is String;
|
||||
}
|
||||
|
||||
class BoardBlockComponentWidget extends BlockComponentStatefulWidget {
|
||||
const BoardBlockComponentWidget({
|
||||
class DatabaseBlockComponentWidget extends BlockComponentStatefulWidget {
|
||||
const DatabaseBlockComponentWidget({
|
||||
super.key,
|
||||
required super.node,
|
||||
super.showActions,
|
||||
@@ -51,11 +55,12 @@ class BoardBlockComponentWidget extends BlockComponentStatefulWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
State<BoardBlockComponentWidget> createState() =>
|
||||
_BoardBlockComponentWidgetState();
|
||||
State<DatabaseBlockComponentWidget> createState() =>
|
||||
_DatabaseBlockComponentWidgetState();
|
||||
}
|
||||
|
||||
class _BoardBlockComponentWidgetState extends State<BoardBlockComponentWidget>
|
||||
class _DatabaseBlockComponentWidgetState
|
||||
extends State<DatabaseBlockComponentWidget>
|
||||
with BlockComponentConfigurable {
|
||||
@override
|
||||
Node get node => widget.node;
|
||||
@@ -70,7 +75,7 @@ class _BoardBlockComponentWidgetState extends State<BoardBlockComponentWidget>
|
||||
node: widget.node,
|
||||
editorState: editorState,
|
||||
builder: (viewPB) {
|
||||
return BoardPage(
|
||||
return DatabaseViewWidget(
|
||||
key: ValueKey(viewPB.id),
|
||||
view: viewPB,
|
||||
);
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/application/doc_bloc.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/insert_page_command.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/selectable_svg_widget.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
SelectionMenuItem inlineGridMenuItem(DocumentBloc documentBloc) =>
|
||||
SelectionMenuItem(
|
||||
name: LocaleKeys.document_slashMenu_grid_createANewGrid.tr(),
|
||||
icon: (editorState, onSelected, style) => SelectableSvgWidget(
|
||||
name: 'editor/grid',
|
||||
isSelected: onSelected,
|
||||
style: style,
|
||||
),
|
||||
keywords: ['grid', 'database'],
|
||||
handler: (editorState, menuService, context) async {
|
||||
if (!documentBloc.view.hasParentViewId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final parentViewId = documentBloc.view.parentViewId;
|
||||
ViewBackendService.createView(
|
||||
parentViewId: parentViewId,
|
||||
openAfterCreate: false,
|
||||
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
layoutType: ViewLayoutPB.Grid,
|
||||
).then(
|
||||
(value) => value
|
||||
.swap()
|
||||
.map((r) => editorState.insertInlinePage(parentViewId, r)),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
SelectionMenuItem inlineBoardMenuItem(DocumentBloc documentBloc) =>
|
||||
SelectionMenuItem(
|
||||
name: LocaleKeys.document_slashMenu_board_createANewBoard.tr(),
|
||||
icon: (editorState, onSelected, style) => SelectableSvgWidget(
|
||||
name: 'editor/board',
|
||||
isSelected: onSelected,
|
||||
style: style,
|
||||
),
|
||||
keywords: ['board', 'kanban', 'database'],
|
||||
handler: (editorState, menuService, context) async {
|
||||
if (!documentBloc.view.hasParentViewId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final parentViewId = documentBloc.view.parentViewId;
|
||||
ViewBackendService.createView(
|
||||
parentViewId: parentViewId,
|
||||
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
layoutType: ViewLayoutPB.Board,
|
||||
).then(
|
||||
(value) => value
|
||||
.swap()
|
||||
.map((r) => editorState.insertInlinePage(parentViewId, r)),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
SelectionMenuItem inlineCalendarMenuItem(DocumentBloc documentBloc) =>
|
||||
SelectionMenuItem(
|
||||
name: LocaleKeys.document_slashMenu_calendar_createANewCalendar.tr(),
|
||||
icon: (editorState, onSelected, style) => SelectableSvgWidget(
|
||||
name: 'editor/calendar',
|
||||
isSelected: onSelected,
|
||||
style: style,
|
||||
),
|
||||
keywords: ['calendar', 'database'],
|
||||
handler: (editorState, menuService, context) async {
|
||||
if (!documentBloc.view.hasParentViewId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final parentViewId = documentBloc.view.parentViewId;
|
||||
ViewBackendService.createView(
|
||||
parentViewId: parentViewId,
|
||||
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
layoutType: ViewLayoutPB.Calendar,
|
||||
).then(
|
||||
(value) => value
|
||||
.swap()
|
||||
.map((r) => editorState.insertInlinePage(parentViewId, r)),
|
||||
);
|
||||
},
|
||||
);
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/link_to_page_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/selectable_svg_widget.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
SelectionMenuItem referencedGridMenuItem = SelectionMenuItem(
|
||||
name: LocaleKeys.document_plugins_referencedGrid.tr(),
|
||||
icon: (editorState, onSelected, style) => SelectableSvgWidget(
|
||||
name: 'editor/grid',
|
||||
isSelected: onSelected,
|
||||
style: style,
|
||||
),
|
||||
keywords: ['referenced', 'grid', 'database'],
|
||||
handler: (editorState, menuService, context) {
|
||||
final container = Overlay.of(context);
|
||||
showLinkToPageMenu(
|
||||
container,
|
||||
editorState,
|
||||
menuService,
|
||||
ViewLayoutPB.Grid,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
SelectionMenuItem referencedBoardMenuItem = SelectionMenuItem(
|
||||
name: LocaleKeys.document_plugins_referencedBoard.tr(),
|
||||
icon: (editorState, onSelected, style) => SelectableSvgWidget(
|
||||
name: 'editor/board',
|
||||
isSelected: onSelected,
|
||||
style: style,
|
||||
),
|
||||
keywords: ['referenced', 'board', 'kanban'],
|
||||
handler: (editorState, menuService, context) {
|
||||
final container = Overlay.of(context);
|
||||
showLinkToPageMenu(
|
||||
container,
|
||||
editorState,
|
||||
menuService,
|
||||
ViewLayoutPB.Board,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
SelectionMenuItem referencedCalendarMenuItem = SelectionMenuItem(
|
||||
name: LocaleKeys.document_plugins_referencedCalendar.tr(),
|
||||
icon: (editorState, onSelected, style) => SelectableSvgWidget(
|
||||
name: 'editor/calendar',
|
||||
isSelected: onSelected,
|
||||
style: style,
|
||||
),
|
||||
keywords: ['referenced', 'calendar', 'database'],
|
||||
handler: (editorState, menuService, context) {
|
||||
showLinkToPageMenu(
|
||||
Overlay.of(context),
|
||||
editorState,
|
||||
menuService,
|
||||
ViewLayoutPB.Calendar,
|
||||
);
|
||||
},
|
||||
);
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/link_to_page_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/selectable_svg_widget.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
SelectionMenuItem referenceGridMenuItem = SelectionMenuItem(
|
||||
name: LocaleKeys.document_plugins_referencedGrid.tr(),
|
||||
icon: (editorState, onSelected, style) => SelectableSvgWidget(
|
||||
name: 'editor/grid',
|
||||
isSelected: onSelected,
|
||||
style: style,
|
||||
),
|
||||
keywords: ['referenced', 'grid', 'database'],
|
||||
handler: (editorState, menuService, context) {
|
||||
final container = Overlay.of(context);
|
||||
showLinkToPageMenu(
|
||||
container,
|
||||
editorState,
|
||||
menuService,
|
||||
ViewLayoutPB.Grid,
|
||||
);
|
||||
},
|
||||
);
|
||||
-90
@@ -1,90 +0,0 @@
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/built_in_page_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/insert_page_command.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class GridBlockKeys {
|
||||
const GridBlockKeys._();
|
||||
|
||||
static const String type = 'grid';
|
||||
}
|
||||
|
||||
class GridBlockComponentBuilder extends BlockComponentBuilder {
|
||||
GridBlockComponentBuilder({
|
||||
this.configuration = const BlockComponentConfiguration(),
|
||||
});
|
||||
|
||||
@override
|
||||
final BlockComponentConfiguration configuration;
|
||||
|
||||
@override
|
||||
BlockComponentWidget build(BlockComponentContext blockComponentContext) {
|
||||
final node = blockComponentContext.node;
|
||||
return GridBlockComponentWidget(
|
||||
key: node.key,
|
||||
node: node,
|
||||
configuration: configuration,
|
||||
showActions: showActions(node),
|
||||
actionBuilder: (context, state) => actionBuilder(
|
||||
blockComponentContext,
|
||||
state,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool validate(Node node) =>
|
||||
node.children.isEmpty &&
|
||||
node.attributes[DatabaseBlockKeys.parentID] is String &&
|
||||
node.attributes[DatabaseBlockKeys.viewID] is String;
|
||||
}
|
||||
|
||||
class GridBlockComponentWidget extends BlockComponentStatefulWidget {
|
||||
const GridBlockComponentWidget({
|
||||
super.key,
|
||||
required super.node,
|
||||
super.showActions,
|
||||
super.actionBuilder,
|
||||
super.configuration = const BlockComponentConfiguration(),
|
||||
});
|
||||
|
||||
@override
|
||||
State<GridBlockComponentWidget> createState() =>
|
||||
_GridBlockComponentWidgetState();
|
||||
}
|
||||
|
||||
class _GridBlockComponentWidgetState extends State<GridBlockComponentWidget>
|
||||
with BlockComponentConfigurable {
|
||||
@override
|
||||
Node get node => widget.node;
|
||||
|
||||
@override
|
||||
BlockComponentConfiguration get configuration => widget.configuration;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final editorState = Provider.of<EditorState>(context, listen: false);
|
||||
Widget child = BuiltInPageWidget(
|
||||
node: widget.node,
|
||||
editorState: editorState,
|
||||
builder: (viewPB) {
|
||||
return GridPage(
|
||||
key: ValueKey(viewPB.id),
|
||||
view: viewPB,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (widget.actionBuilder != null) {
|
||||
child = BlockComponentActionWrapper(
|
||||
node: widget.node,
|
||||
actionBuilder: widget.actionBuilder!,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/application/doc_bloc.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/insert_page_command.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/selectable_svg_widget.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
SelectionMenuItem inlineGridMenuItem(DocumentBloc documentBloc) =>
|
||||
SelectionMenuItem(
|
||||
name: LocaleKeys.document_slashMenu_grid_createANewGrid.tr(),
|
||||
icon: (editorState, onSelected, style) => SelectableSvgWidget(
|
||||
name: 'editor/grid',
|
||||
isSelected: onSelected,
|
||||
style: style,
|
||||
),
|
||||
keywords: ['grid', 'database'],
|
||||
handler: (editorState, menuService, context) async {
|
||||
if (!documentBloc.view.hasParentViewId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final parentViewId = documentBloc.view.parentViewId;
|
||||
ViewBackendService.createView(
|
||||
parentViewId: parentViewId,
|
||||
openAfterCreate: false,
|
||||
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
layoutType: ViewLayoutPB.Grid,
|
||||
).then(
|
||||
(value) => value
|
||||
.swap()
|
||||
.map((r) => editorState.insertInlinePage(parentViewId, r)),
|
||||
);
|
||||
},
|
||||
);
|
||||
+2
-6
@@ -1,6 +1,3 @@
|
||||
export 'board/board_node_widget.dart';
|
||||
export 'board/board_menu_item.dart';
|
||||
export 'board/board_view_menu_item.dart';
|
||||
export 'callout/callout_block_component.dart';
|
||||
export 'code_block/code_block_component.dart';
|
||||
export 'code_block/code_block_shortcut_event.dart';
|
||||
@@ -9,9 +6,8 @@ export 'cover/cover_node_widget.dart';
|
||||
export 'cover/cover_image_picker.dart';
|
||||
export 'emoji_picker/emoji_menu_item.dart';
|
||||
export 'extensions/flowy_tint_extension.dart';
|
||||
export 'grid/grid_menu_item.dart';
|
||||
export 'grid/grid_node_widget.dart';
|
||||
export 'grid/grid_view_menu_item.dart';
|
||||
export 'database/inline_database_menu_item.dart';
|
||||
export 'database/database_view_block_component.dart';
|
||||
export 'math_equation/math_equation_block_component.dart';
|
||||
export 'openai/widgets/auto_completion_node_widget.dart';
|
||||
export 'openai/widgets/smart_edit_node_widget.dart';
|
||||
|
||||
+20
-29
@@ -1,26 +1,25 @@
|
||||
library document_plugin;
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/plugins/document/application/share_bloc.dart';
|
||||
import 'package:appflowy/util/file_picker/file_picker_service.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-document2/entities.pb.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:clipboard/clipboard.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class DocumentShareButton extends StatelessWidget {
|
||||
const DocumentShareButton({
|
||||
super.key,
|
||||
required this.view,
|
||||
});
|
||||
|
||||
final ViewPB view;
|
||||
DocumentShareButton({Key? key, required this.view})
|
||||
: super(key: ValueKey(view.hashCode));
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -28,12 +27,10 @@ class DocumentShareButton extends StatelessWidget {
|
||||
create: (context) => getIt<DocShareBloc>(param1: view),
|
||||
child: BlocListener<DocShareBloc, DocShareState>(
|
||||
listener: (context, state) {
|
||||
state.map(
|
||||
initial: (_) {},
|
||||
loading: (_) {},
|
||||
state.mapOrNull(
|
||||
finish: (state) {
|
||||
state.successOrFail.fold(
|
||||
_handleExportData,
|
||||
(data) => _handleExportData(context, data),
|
||||
_handleExportError,
|
||||
);
|
||||
},
|
||||
@@ -52,27 +49,30 @@ class DocumentShareButton extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
void _handleExportData(ExportDataPB exportData) {
|
||||
void _handleExportData(BuildContext context, ExportDataPB exportData) {
|
||||
switch (exportData.exportType) {
|
||||
case ExportType.Link:
|
||||
break;
|
||||
case ExportType.Markdown:
|
||||
FlutterClipboard.copy(exportData.data)
|
||||
.then((value) => Log.info('copied to clipboard'));
|
||||
showSnackBarMessage(
|
||||
context,
|
||||
LocaleKeys.settings_files_exportFileSuccess.tr(),
|
||||
);
|
||||
break;
|
||||
case ExportType.Link:
|
||||
case ExportType.Text:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _handleExportError(FlowyError error) {}
|
||||
void _handleExportError(FlowyError error) {
|
||||
showMessageToast(error.msg);
|
||||
}
|
||||
}
|
||||
|
||||
class ShareActionList extends StatelessWidget {
|
||||
const ShareActionList({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.view,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final ViewPB view;
|
||||
|
||||
@@ -94,20 +94,14 @@ class ShareActionList extends StatelessWidget {
|
||||
onSelected: (action, controller) async {
|
||||
switch (action.inner) {
|
||||
case ShareAction.markdown:
|
||||
final exportPath = await FilePicker.platform.saveFile(
|
||||
final exportPath = await getIt<FilePickerService>().saveFile(
|
||||
dialogTitle: '',
|
||||
fileName: '${view.name}.md',
|
||||
);
|
||||
if (exportPath != null) {
|
||||
docShareBloc.add(DocShareEvent.shareMarkdown(exportPath));
|
||||
showMessageToast('Exported to: $exportPath');
|
||||
}
|
||||
break;
|
||||
// case ShareAction.copyLink:
|
||||
// NavigatorAlertDialog(
|
||||
// title: LocaleKeys.shareAction_workInProgress.tr(),
|
||||
// ).show(context);
|
||||
// break;
|
||||
}
|
||||
controller.close();
|
||||
},
|
||||
@@ -117,7 +111,6 @@ class ShareActionList extends StatelessWidget {
|
||||
|
||||
enum ShareAction {
|
||||
markdown,
|
||||
// copyLink,
|
||||
}
|
||||
|
||||
class ShareActionWrapper extends ActionCell {
|
||||
@@ -132,8 +125,6 @@ class ShareActionWrapper extends ActionCell {
|
||||
switch (inner) {
|
||||
case ShareAction.markdown:
|
||||
return LocaleKeys.shareAction_markdown.tr();
|
||||
// case ShareAction.copyLink:
|
||||
// return LocaleKeys.shareAction_copyLink.tr();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ class TrashPluginDisplay extends PluginWidgetBuilder {
|
||||
Widget? get rightBarItem => null;
|
||||
|
||||
@override
|
||||
Widget buildWidget(PluginContext context) => const TrashPage(
|
||||
Widget buildWidget({PluginContext? context}) => const TrashPage(
|
||||
key: ValueKey('TrashPage'),
|
||||
);
|
||||
|
||||
|
||||
@@ -110,9 +110,8 @@ void _resolveHomeDeps(GetIt getIt) {
|
||||
);
|
||||
|
||||
// share
|
||||
getIt.registerLazySingleton<ShareService>(() => ShareService());
|
||||
getIt.registerFactoryParam<DocShareBloc, ViewPB, void>(
|
||||
(view, _) => DocShareBloc(view: view, service: getIt<ShareService>()),
|
||||
(view, _) => DocShareBloc(view: view),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ abstract class PluginWidgetBuilder with NavigationItem {
|
||||
EdgeInsets get contentPadding =>
|
||||
const EdgeInsets.symmetric(horizontal: 40, vertical: 28);
|
||||
|
||||
Widget buildWidget(PluginContext context);
|
||||
Widget buildWidget({PluginContext? context});
|
||||
}
|
||||
|
||||
class PluginContext {
|
||||
|
||||
@@ -34,4 +34,23 @@ class FilePicker implements FilePickerService {
|
||||
);
|
||||
return FilePickerResult(result?.files ?? []);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> saveFile({
|
||||
String? dialogTitle,
|
||||
String? fileName,
|
||||
String? initialDirectory,
|
||||
fp.FileType type = fp.FileType.any,
|
||||
List<String>? allowedExtensions,
|
||||
bool lockParentWindow = false,
|
||||
}) {
|
||||
return fp.FilePicker.platform.saveFile(
|
||||
dialogTitle: dialogTitle,
|
||||
fileName: fileName,
|
||||
initialDirectory: initialDirectory,
|
||||
type: type,
|
||||
allowedExtensions: allowedExtensions,
|
||||
lockParentWindow: lockParentWindow,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -27,4 +27,14 @@ abstract class FilePickerService {
|
||||
bool lockParentWindow = false,
|
||||
}) async =>
|
||||
throw UnimplementedError('pickFiles() has not been implemented.');
|
||||
|
||||
Future<String?> saveFile({
|
||||
String? dialogTitle,
|
||||
String? fileName,
|
||||
String? initialDirectory,
|
||||
FileType type = FileType.any,
|
||||
List<String>? allowedExtensions,
|
||||
bool lockParentWindow = false,
|
||||
}) async =>
|
||||
throw UnimplementedError('saveFile() has not been implemented.');
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/application/document_data_pb_extension.dart';
|
||||
import 'package:appflowy/plugins/document/application/prelude.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/code_block_node_parser.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/divider_node_parser.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/math_equation_node_parser.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
enum DocumentExportType {
|
||||
json,
|
||||
markdown,
|
||||
text,
|
||||
}
|
||||
|
||||
class DocumentExporter {
|
||||
const DocumentExporter(
|
||||
this.view,
|
||||
);
|
||||
|
||||
final ViewPB view;
|
||||
|
||||
Future<Either<FlowyError, String>> export(DocumentExportType type) async {
|
||||
final documentService = DocumentService();
|
||||
final result = await documentService.openDocument(view: view);
|
||||
return result.fold((error) => left(error), (r) {
|
||||
final document = r.toDocument();
|
||||
if (document == null) {
|
||||
return left(
|
||||
FlowyError(
|
||||
msg: LocaleKeys.settings_files_exportFileFail.tr(),
|
||||
),
|
||||
);
|
||||
}
|
||||
switch (type) {
|
||||
case DocumentExportType.json:
|
||||
return right(jsonEncode(document));
|
||||
case DocumentExportType.markdown:
|
||||
final markdown = documentToMarkdown(
|
||||
document,
|
||||
customParsers: [
|
||||
const DividerNodeParser(),
|
||||
const MathEquationNodeParser(),
|
||||
const CodeBlockNodeParser(),
|
||||
],
|
||||
);
|
||||
return right(markdown);
|
||||
case DocumentExportType.text:
|
||||
throw UnimplementedError();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -180,7 +180,7 @@ class HomeStackManager {
|
||||
if (pluginType == notifier.plugin.pluginType) {
|
||||
final builder = notifier.plugin.widgetBuilder;
|
||||
final pluginWidget = builder.buildWidget(
|
||||
PluginContext(onDeleted: onDeleted),
|
||||
context: PluginContext(onDeleted: onDeleted),
|
||||
);
|
||||
|
||||
return Padding(
|
||||
|
||||
@@ -39,3 +39,14 @@ void showMessageToast(String message) {
|
||||
toastDuration: const Duration(seconds: 3),
|
||||
);
|
||||
}
|
||||
|
||||
void showSnackBarMessage(BuildContext context, String message) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: FlowyText(
|
||||
message,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
+4
-2
@@ -70,8 +70,10 @@ class SettingsFileLocationCustomizerState
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
_ChangeStoragePathButton(
|
||||
usingPath: path,
|
||||
Flexible(
|
||||
child: _ChangeStoragePathButton(
|
||||
usingPath: path,
|
||||
),
|
||||
),
|
||||
const HSpace(10),
|
||||
_OpenStorageButton(
|
||||
|
||||
+7
-10
@@ -1,10 +1,8 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/plugins/document/application/document_data_pb_extension.dart';
|
||||
import 'package:appflowy/plugins/document/application/prelude.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/util/file_picker/file_picker_service.dart';
|
||||
import 'package:appflowy/workspace/application/export/document_exporter.dart';
|
||||
import 'package:appflowy/workspace/application/settings/settings_file_exporter_cubit.dart';
|
||||
import 'package:appflowy/workspace/application/settings/share/export_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
@@ -248,18 +246,17 @@ class _AppFlowyFileExporter {
|
||||
) async {
|
||||
final failedFileNames = <String>[];
|
||||
final Map<String, int> names = {};
|
||||
final documentService = DocumentService();
|
||||
for (final view in views) {
|
||||
String? content;
|
||||
String? fileExtension;
|
||||
switch (view.layout) {
|
||||
case ViewLayoutPB.Document:
|
||||
final document = await documentService.openDocument(view: view);
|
||||
document.fold((l) => Log.error(l), (r) {
|
||||
final json = r.toDocument()?.toJson();
|
||||
if (json != null) {
|
||||
content = jsonEncode(json);
|
||||
}
|
||||
final documentExporter = DocumentExporter(view);
|
||||
final result = await documentExporter.export(
|
||||
DocumentExportType.json,
|
||||
);
|
||||
result.fold((l) => Log.error(l), (json) {
|
||||
content = json;
|
||||
});
|
||||
fileExtension = 'afdocument';
|
||||
break;
|
||||
|
||||
@@ -285,10 +285,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: csslib
|
||||
sha256: "831883fb353c8bdc1d71979e5b342c7d88acfbc643113c14ae51e2442ea0f20f"
|
||||
sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.17.3"
|
||||
version: "1.0.0"
|
||||
dart_style:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -648,10 +648,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: html
|
||||
sha256: "58e3491f7bf0b6a4ea5110c0c688877460d1a6366731155c4a4580e7ded773e8"
|
||||
sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.15.3"
|
||||
version: "0.15.4"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 0.2.0
|
||||
version: 0.2.1
|
||||
|
||||
environment:
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
|
||||
+6
-5
@@ -48,8 +48,9 @@ void main() {
|
||||
);
|
||||
final expectedGroupName = "No ${multiSelectField.name}";
|
||||
assert(
|
||||
boardBloc.groupControllers.values.first.group.desc == expectedGroupName,
|
||||
"Expected $expectedGroupName, but receive ${boardBloc.groupControllers.values.first.group.desc}",
|
||||
boardBloc.groupControllers.values.first.group.groupName ==
|
||||
expectedGroupName,
|
||||
"Expected $expectedGroupName, but receive ${boardBloc.groupControllers.values.first.group.groupName}",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -101,8 +102,8 @@ void main() {
|
||||
|
||||
final groups =
|
||||
boardBloc.groupControllers.values.map((e) => e.group).toList();
|
||||
assert(groups[0].desc == "No ${multiSelectField.name}");
|
||||
assert(groups[1].desc == "B");
|
||||
assert(groups[2].desc == "A");
|
||||
assert(groups[0].groupName == "No ${multiSelectField.name}");
|
||||
assert(groups[1].groupName == "B");
|
||||
assert(groups[2].groupName == "A");
|
||||
});
|
||||
}
|
||||
+10
-10
@@ -99,7 +99,7 @@ checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
|
||||
[[package]]
|
||||
name = "appflowy-integrate"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4f5837#4f58377a1411745577c216a34672c49dc17413ec"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
@@ -1024,7 +1024,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4f5837#4f58377a1411745577c216a34672c49dc17413ec"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -1042,7 +1042,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-client-ws"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4f5837#4f58377a1411745577c216a34672c49dc17413ec"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"collab-sync",
|
||||
@@ -1060,7 +1060,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-database"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4f5837#4f58377a1411745577c216a34672c49dc17413ec"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -1086,7 +1086,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-derive"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4f5837#4f58377a1411745577c216a34672c49dc17413ec"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1098,7 +1098,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-document"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4f5837#4f58377a1411745577c216a34672c49dc17413ec"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
@@ -1115,7 +1115,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-folder"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4f5837#4f58377a1411745577c216a34672c49dc17413ec"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -1135,7 +1135,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-persistence"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4f5837#4f58377a1411745577c216a34672c49dc17413ec"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"chrono",
|
||||
@@ -1155,7 +1155,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-plugins"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4f5837#4f58377a1411745577c216a34672c49dc17413ec"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -1186,7 +1186,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-sync"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4f5837#4f58377a1411745577c216a34672c49dc17413ec"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"collab",
|
||||
|
||||
@@ -34,12 +34,12 @@ default = ["custom-protocol"]
|
||||
custom-protocol = ["tauri/custom-protocol"]
|
||||
|
||||
[patch.crates-io]
|
||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" }
|
||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" }
|
||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" }
|
||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" }
|
||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" }
|
||||
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" }
|
||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4f5837" }
|
||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4f5837" }
|
||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4f5837" }
|
||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4f5837" }
|
||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4f5837" }
|
||||
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4f5837" }
|
||||
|
||||
#collab = { path = "../../AppFlowy-Collab/collab" }
|
||||
#collab-folder = { path = "../../AppFlowy-Collab/collab-folder" }
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
[toolchain]
|
||||
channel = "1.65"
|
||||
channel = "1.70"
|
||||
+2
-2
@@ -24,7 +24,7 @@ export class DateCellDataPersistence extends CellDataPersistence<CalendarData> {
|
||||
}
|
||||
|
||||
save(data: CalendarData): Promise<Result<void, FlowyError>> {
|
||||
const payload = DateChangesetPB.fromObject({ cell_path: _makeCellPath(this.cellIdentifier) });
|
||||
const payload = DateChangesetPB.fromObject({ cell_id: _makeCellId(this.cellIdentifier) });
|
||||
payload.date = ((data.date.getTime() / 1000) | 0).toString();
|
||||
if (data.time !== undefined) {
|
||||
payload.time = data.time;
|
||||
@@ -34,7 +34,7 @@ export class DateCellDataPersistence extends CellDataPersistence<CalendarData> {
|
||||
}
|
||||
}
|
||||
|
||||
function _makeCellPath(cellIdentifier: CellIdentifier): CellIdPB {
|
||||
function _makeCellId(cellIdentifier: CellIdentifier): CellIdPB {
|
||||
return CellIdPB.fromObject({
|
||||
view_id: cellIdentifier.viewId,
|
||||
field_id: cellIdentifier.fieldId,
|
||||
|
||||
+10
-11
@@ -1,9 +1,9 @@
|
||||
import { DatabaseNotification, FlowyError, GroupPB, GroupRowsNotificationPB, RowPB } from "@/services/backend";
|
||||
import { ChangeNotifier } from "$app/utils/change_notifier";
|
||||
import { None, Ok, Option, Result, Some } from "ts-results";
|
||||
import { DatabaseNotificationObserver } from "../notifications/observer";
|
||||
import { Log } from "$app/utils/log";
|
||||
import { DatabaseBackendService } from "../database_bd_svc";
|
||||
import { DatabaseNotification, FlowyError, GroupPB, GroupRowsNotificationPB, RowPB } from '@/services/backend';
|
||||
import { ChangeNotifier } from '$app/utils/change_notifier';
|
||||
import { None, Ok, Option, Result, Some } from 'ts-results';
|
||||
import { DatabaseNotificationObserver } from '../notifications/observer';
|
||||
import { Log } from '$app/utils/log';
|
||||
import { DatabaseBackendService } from '../database_bd_svc';
|
||||
|
||||
export type GroupDataCallbacks = {
|
||||
onRemoveRow: (groupId: string, rowId: string) => void;
|
||||
@@ -30,7 +30,7 @@ export class DatabaseGroupController {
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.group.desc;
|
||||
return this.group.group_name;
|
||||
}
|
||||
|
||||
updateGroup = (group: GroupPB) => {
|
||||
@@ -83,7 +83,7 @@ export class DatabaseGroupController {
|
||||
} else {
|
||||
Log.error(result.val);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -111,8 +111,7 @@ class GroupDataObserver {
|
||||
private notifier?: ChangeNotifier<Result<GroupRowsNotificationPB, FlowyError>>;
|
||||
private listener?: DatabaseNotificationObserver;
|
||||
|
||||
constructor(public readonly groupId: string) {
|
||||
}
|
||||
constructor(public readonly groupId: string) {}
|
||||
|
||||
subscribe = async (callbacks: { onRowsChanged: GroupRowsSubscribeCallback }) => {
|
||||
this.notifier = new ChangeNotifier();
|
||||
@@ -132,7 +131,7 @@ class GroupDataObserver {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
await this.listener.start();
|
||||
};
|
||||
|
||||
Generated
+10
-10
@@ -85,7 +85,7 @@ checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
|
||||
[[package]]
|
||||
name = "appflowy-integrate"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4f5837#4f58377a1411745577c216a34672c49dc17413ec"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
@@ -887,7 +887,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4f5837#4f58377a1411745577c216a34672c49dc17413ec"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -905,7 +905,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-client-ws"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4f5837#4f58377a1411745577c216a34672c49dc17413ec"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"collab-sync",
|
||||
@@ -923,7 +923,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-database"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4f5837#4f58377a1411745577c216a34672c49dc17413ec"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -949,7 +949,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-derive"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4f5837#4f58377a1411745577c216a34672c49dc17413ec"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -961,7 +961,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-document"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4f5837#4f58377a1411745577c216a34672c49dc17413ec"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
@@ -978,7 +978,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-folder"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4f5837#4f58377a1411745577c216a34672c49dc17413ec"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -998,7 +998,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-persistence"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4f5837#4f58377a1411745577c216a34672c49dc17413ec"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"chrono",
|
||||
@@ -1018,7 +1018,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-plugins"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4f5837#4f58377a1411745577c216a34672c49dc17413ec"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -1049,7 +1049,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-sync"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4f5837#4f58377a1411745577c216a34672c49dc17413ec"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"collab",
|
||||
|
||||
@@ -33,11 +33,11 @@ opt-level = 3
|
||||
incremental = false
|
||||
|
||||
[patch.crates-io]
|
||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" }
|
||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" }
|
||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" }
|
||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" }
|
||||
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" }
|
||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4f5837" }
|
||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4f5837" }
|
||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4f5837" }
|
||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4f5837" }
|
||||
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4f5837" }
|
||||
|
||||
#collab = { path = "../AppFlowy-Collab/collab" }
|
||||
#collab-folder = { path = "../AppFlowy-Collab/collab-folder" }
|
||||
|
||||
@@ -58,7 +58,7 @@ pub extern "C" fn async_event(port: i64, input: *const u8, len: usize) {
|
||||
},
|
||||
Some(e) => e.event_dispatcher.clone(),
|
||||
};
|
||||
let _ = AFPluginDispatcher::async_send_with_callback(
|
||||
AFPluginDispatcher::async_send_with_callback(
|
||||
dispatcher,
|
||||
request,
|
||||
move |resp: AFPluginEventResponse| {
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use lib_dispatch::prelude::{AFPluginEventResponse, Payload, StatusCode};
|
||||
|
||||
#[derive(ProtoBuf_Enum, Clone, Copy)]
|
||||
#[derive(ProtoBuf_Enum, Clone, Copy, Default)]
|
||||
pub enum FFIStatusCode {
|
||||
#[default]
|
||||
Ok = 0,
|
||||
Err = 1,
|
||||
Internal = 2,
|
||||
}
|
||||
|
||||
impl std::default::Default for FFIStatusCode {
|
||||
fn default() -> FFIStatusCode {
|
||||
FFIStatusCode::Ok
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ProtoBuf, Default)]
|
||||
pub struct FFIResponse {
|
||||
#[pb(index = 1)]
|
||||
|
||||
@@ -45,7 +45,7 @@ impl SnapshotPersistence for SnapshotDBImpl {
|
||||
.map_err(|e| PersistenceError::Internal(Box::new(e)))?,
|
||||
);
|
||||
|
||||
let _ = tokio::task::spawn_blocking(move || {
|
||||
tokio::task::spawn_blocking(move || {
|
||||
if let Some(pool) = weak_pool.upgrade() {
|
||||
let conn = pool
|
||||
.get()
|
||||
|
||||
@@ -80,17 +80,11 @@ impl std::convert::From<CalendarLayout> for CalendarLayoutPB {
|
||||
pub struct CalendarEventRequestPB {
|
||||
#[pb(index = 1)]
|
||||
pub view_id: String,
|
||||
|
||||
// Currently, requesting the events within the specified month
|
||||
// is not supported
|
||||
#[pb(index = 2)]
|
||||
pub month: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct CalendarEventRequestParams {
|
||||
pub view_id: String,
|
||||
pub month: String,
|
||||
}
|
||||
|
||||
impl TryInto<CalendarEventRequestParams> for CalendarEventRequestPB {
|
||||
@@ -98,10 +92,7 @@ impl TryInto<CalendarEventRequestParams> for CalendarEventRequestPB {
|
||||
|
||||
fn try_into(self) -> Result<CalendarEventRequestParams, Self::Error> {
|
||||
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::ViewIdIsInvalid)?;
|
||||
Ok(CalendarEventRequestParams {
|
||||
view_id: view_id.0,
|
||||
month: self.month,
|
||||
})
|
||||
Ok(CalendarEventRequestParams { view_id: view_id.0 })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -135,11 +135,13 @@ impl TryInto<MoveRowParams> for MoveRowPayloadPB {
|
||||
|
||||
fn try_into(self) -> Result<MoveRowParams, Self::Error> {
|
||||
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseViewIdIsEmpty)?;
|
||||
let from_row_id = NotEmptyStr::parse(self.from_row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
|
||||
let to_row_id = NotEmptyStr::parse(self.to_row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
|
||||
|
||||
Ok(MoveRowParams {
|
||||
view_id: view_id.0,
|
||||
from_row_id: RowId::from(self.from_row_id),
|
||||
to_row_id: RowId::from(self.to_row_id),
|
||||
from_row_id: RowId::from(from_row_id.0),
|
||||
to_row_id: RowId::from(to_row_id.0),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,7 +475,9 @@ pub struct FieldChangesetParams {
|
||||
Deserialize_repr,
|
||||
)]
|
||||
#[repr(u8)]
|
||||
#[derive(Default)]
|
||||
pub enum FieldType {
|
||||
#[default]
|
||||
RichText = 0,
|
||||
Number = 1,
|
||||
DateTime = 2,
|
||||
@@ -488,12 +490,6 @@ pub enum FieldType {
|
||||
CreatedTime = 9,
|
||||
}
|
||||
|
||||
impl std::default::Default for FieldType {
|
||||
fn default() -> Self {
|
||||
FieldType::RichText
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for FieldType {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
let value: i64 = self.clone().into();
|
||||
|
||||
@@ -10,7 +10,9 @@ pub struct CheckboxFilterPB {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
|
||||
#[repr(u8)]
|
||||
#[derive(Default)]
|
||||
pub enum CheckboxFilterConditionPB {
|
||||
#[default]
|
||||
IsChecked = 0,
|
||||
IsUnChecked = 1,
|
||||
}
|
||||
@@ -21,12 +23,6 @@ impl std::convert::From<CheckboxFilterConditionPB> for u32 {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::default::Default for CheckboxFilterConditionPB {
|
||||
fn default() -> Self {
|
||||
CheckboxFilterConditionPB::IsChecked
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<u8> for CheckboxFilterConditionPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
|
||||
@@ -10,8 +10,10 @@ pub struct ChecklistFilterPB {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
|
||||
#[repr(u8)]
|
||||
#[derive(Default)]
|
||||
pub enum ChecklistFilterConditionPB {
|
||||
IsComplete = 0,
|
||||
#[default]
|
||||
IsIncomplete = 1,
|
||||
}
|
||||
|
||||
@@ -21,12 +23,6 @@ impl std::convert::From<ChecklistFilterConditionPB> for u32 {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::default::Default for ChecklistFilterConditionPB {
|
||||
fn default() -> Self {
|
||||
ChecklistFilterConditionPB::IsIncomplete
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<u8> for ChecklistFilterConditionPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
|
||||
@@ -42,7 +42,9 @@ impl FromStr for DateFilterContentPB {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
|
||||
#[repr(u8)]
|
||||
#[derive(Default)]
|
||||
pub enum DateFilterConditionPB {
|
||||
#[default]
|
||||
DateIs = 0,
|
||||
DateBefore = 1,
|
||||
DateAfter = 2,
|
||||
@@ -58,11 +60,6 @@ impl std::convert::From<DateFilterConditionPB> for u32 {
|
||||
value as u32
|
||||
}
|
||||
}
|
||||
impl std::default::Default for DateFilterConditionPB {
|
||||
fn default() -> Self {
|
||||
DateFilterConditionPB::DateIs
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<u8> for DateFilterConditionPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
@@ -13,7 +13,9 @@ pub struct NumberFilterPB {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
|
||||
#[repr(u8)]
|
||||
#[derive(Default)]
|
||||
pub enum NumberFilterConditionPB {
|
||||
#[default]
|
||||
Equal = 0,
|
||||
NotEqual = 1,
|
||||
GreaterThan = 2,
|
||||
@@ -24,12 +26,6 @@ pub enum NumberFilterConditionPB {
|
||||
NumberIsNotEmpty = 7,
|
||||
}
|
||||
|
||||
impl std::default::Default for NumberFilterConditionPB {
|
||||
fn default() -> Self {
|
||||
NumberFilterConditionPB::Equal
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<NumberFilterConditionPB> for u32 {
|
||||
fn from(value: NumberFilterConditionPB) -> Self {
|
||||
value as u32
|
||||
|
||||
+2
-6
@@ -14,7 +14,9 @@ pub struct SelectOptionFilterPB {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
|
||||
#[repr(u8)]
|
||||
#[derive(Default)]
|
||||
pub enum SelectOptionConditionPB {
|
||||
#[default]
|
||||
OptionIs = 0,
|
||||
OptionIsNot = 1,
|
||||
OptionIsEmpty = 2,
|
||||
@@ -27,12 +29,6 @@ impl std::convert::From<SelectOptionConditionPB> for u32 {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::default::Default for SelectOptionConditionPB {
|
||||
fn default() -> Self {
|
||||
SelectOptionConditionPB::OptionIs
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<u8> for SelectOptionConditionPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
|
||||
@@ -13,7 +13,9 @@ pub struct TextFilterPB {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
|
||||
#[repr(u8)]
|
||||
#[derive(Default)]
|
||||
pub enum TextFilterConditionPB {
|
||||
#[default]
|
||||
Is = 0,
|
||||
IsNot = 1,
|
||||
Contains = 2,
|
||||
@@ -30,12 +32,6 @@ impl std::convert::From<TextFilterConditionPB> for u32 {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::default::Default for TextFilterConditionPB {
|
||||
fn default() -> Self {
|
||||
TextFilterConditionPB::Is
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<u8> for TextFilterConditionPB {
|
||||
type Error = ErrorCode;
|
||||
|
||||
|
||||
@@ -54,7 +54,9 @@ pub struct DateGroupConfigurationPB {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
|
||||
#[repr(u8)]
|
||||
#[derive(Default)]
|
||||
pub enum DateCondition {
|
||||
#[default]
|
||||
Relative = 0,
|
||||
Day = 1,
|
||||
Week = 2,
|
||||
@@ -62,12 +64,6 @@ pub enum DateCondition {
|
||||
Year = 4,
|
||||
}
|
||||
|
||||
impl std::default::Default for DateCondition {
|
||||
fn default() -> Self {
|
||||
DateCondition::Relative
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
|
||||
pub struct CheckboxGroupConfigurationPB {
|
||||
#[pb(index = 1)]
|
||||
|
||||
@@ -76,7 +76,7 @@ pub struct GroupPB {
|
||||
pub group_id: String,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub desc: String,
|
||||
pub group_name: String,
|
||||
|
||||
#[pb(index = 4)]
|
||||
pub rows: Vec<RowPB>,
|
||||
@@ -93,7 +93,7 @@ impl std::convert::From<GroupData> for GroupPB {
|
||||
Self {
|
||||
field_id: group_data.field_id,
|
||||
group_id: group_data.id,
|
||||
desc: group_data.name,
|
||||
group_name: group_data.name,
|
||||
rows: group_data.rows.into_iter().map(RowPB::from).collect(),
|
||||
is_default: group_data.is_default,
|
||||
is_visible: group_data.is_visible,
|
||||
@@ -108,9 +108,6 @@ pub struct GroupByFieldPayloadPB {
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub view_id: String,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub field_type: FieldType,
|
||||
}
|
||||
|
||||
impl TryInto<GroupByFieldParams> for GroupByFieldPayloadPB {
|
||||
@@ -124,18 +121,13 @@ impl TryInto<GroupByFieldParams> for GroupByFieldPayloadPB {
|
||||
.map_err(|_| ErrorCode::ViewIdIsInvalid)?
|
||||
.0;
|
||||
|
||||
Ok(GroupByFieldParams {
|
||||
field_id,
|
||||
view_id,
|
||||
field_type: self.field_type,
|
||||
})
|
||||
Ok(GroupByFieldParams { field_id, view_id })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GroupByFieldParams {
|
||||
pub field_id: String,
|
||||
pub view_id: String,
|
||||
pub field_type: FieldType,
|
||||
}
|
||||
|
||||
pub struct DeleteGroupParams {
|
||||
|
||||
@@ -158,6 +158,7 @@ impl TryInto<RowIdParams> for RowIdPB {
|
||||
|
||||
fn try_into(self) -> Result<RowIdParams, Self::Error> {
|
||||
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseIdIsEmpty)?;
|
||||
let row_id = NotEmptyStr::parse(self.row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
|
||||
let group_id = match self.group_id {
|
||||
Some(group_id) => Some(
|
||||
NotEmptyStr::parse(group_id)
|
||||
@@ -169,7 +170,7 @@ impl TryInto<RowIdParams> for RowIdPB {
|
||||
|
||||
Ok(RowIdParams {
|
||||
view_id: view_id.0,
|
||||
row_id: RowId::from(self.row_id),
|
||||
row_id: RowId::from(row_id.0),
|
||||
group_id,
|
||||
})
|
||||
}
|
||||
|
||||
Loaded 100 of 150 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user